/**
 * Repository tests for subscriptions module — in-memory datastore.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { MemoryDatastoreProvider } from '@bytelyst/datastore';
import { setProvider } from '../../lib/datastore.js';
import {
  getByUserId,
  getByStripeCustomerIdAnyProduct,
  getByStripeCustomerId,
  createSubscription,
  updateSubscription,
  getPaymentsByUser,
  createPayment,
} from './repository.js';
import type { SubscriptionDoc, PaymentDoc } from './types.js';

const baseSub: SubscriptionDoc = {
  id: 'sub_1',
  productId: 'lysnrai',
  userId: 'user_1',
  plan: 'pro',
  status: 'active',
  currentPeriodStart: '2026-02-01T00:00:00Z',
  currentPeriodEnd: '2026-03-01T00:00:00Z',
  cancelAtPeriodEnd: false,
  monthlyPrice: 9.99,
  tokensIncluded: 100000,
  tokensUsed: 5000,
  stripeCustomerId: 'cus_abc',
  createdAt: '2026-02-01T00:00:00Z',
  updatedAt: '2026-02-01T00:00:00Z',
};

const basePayment: PaymentDoc = {
  id: 'pay_1',
  productId: 'lysnrai',
  userId: 'user_1',
  amount: 999,
  currency: 'usd',
  status: 'succeeded',
  description: 'Pro plan',
  method: 'card',
  createdAt: '2026-02-01T00:00:00Z',
};

describe('subscriptions repository', () => {
  beforeEach(() => {
    setProvider(new MemoryDatastoreProvider());
  });

  describe('getByUserId', () => {
    it('returns subscription when found', async () => {
      await createSubscription(baseSub);
      const result = await getByUserId('user_1', 'lysnrai');
      expect(result).not.toBeNull();
      expect(result!.id).toBe('sub_1');
    });

    it('returns null when not found', async () => {
      const result = await getByUserId('user_1', 'lysnrai');
      expect(result).toBeNull();
    });
  });

  describe('getByStripeCustomerIdAnyProduct', () => {
    it('returns subscription when found', async () => {
      await createSubscription(baseSub);
      const result = await getByStripeCustomerIdAnyProduct('cus_abc');
      expect(result).not.toBeNull();
      expect(result!.stripeCustomerId).toBe('cus_abc');
    });

    it('returns null when not found', async () => {
      const result = await getByStripeCustomerIdAnyProduct('cus_none');
      expect(result).toBeNull();
    });
  });

  describe('getByStripeCustomerId', () => {
    it('returns subscription when found', async () => {
      await createSubscription(baseSub);
      const result = await getByStripeCustomerId('cus_abc', 'lysnrai');
      expect(result).not.toBeNull();
      expect(result!.stripeCustomerId).toBe('cus_abc');
    });

    it('returns null when not found', async () => {
      const result = await getByStripeCustomerId('cus_none', 'lysnrai');
      expect(result).toBeNull();
    });
  });

  describe('createSubscription', () => {
    it('creates and returns subscription', async () => {
      const result = await createSubscription(baseSub);
      expect(result.id).toBe('sub_1');
      expect(result.plan).toBe('pro');
    });
  });

  describe('updateSubscription', () => {
    it('merges updates and returns subscription', async () => {
      await createSubscription(baseSub);
      const result = await updateSubscription('sub_1', 'user_1', { plan: 'enterprise' });
      expect(result).not.toBeNull();
      expect(result!.plan).toBe('enterprise');
    });

    it('returns null when not found', async () => {
      const result = await updateSubscription('sub_1', 'user_1', { plan: 'enterprise' });
      expect(result).toBeNull();
    });
  });

  describe('getPaymentsByUser', () => {
    it('returns payments for user', async () => {
      await createPayment(basePayment);
      const result = await getPaymentsByUser('user_1', 'lysnrai');
      expect(result).toHaveLength(1);
      expect(result[0].id).toBe('pay_1');
    });

    it('returns empty array when no payments', async () => {
      const result = await getPaymentsByUser('user_1', 'lysnrai');
      expect(result).toEqual([]);
    });
  });

  describe('createPayment', () => {
    it('creates and returns payment', async () => {
      const result = await createPayment(basePayment);
      expect(result.id).toBe('pay_1');
      expect(result.amount).toBe(999);
    });
  });
});
