import { Test, TestingModule } from '@nestjs/testing';
import { EffectiveMembershipService } from './effective-membership.service';
import { PrismaService } from '../../infrastructure/database/prisma.service';

describe('EffectiveMembershipService', () => {
  let service: EffectiveMembershipService;
  let prisma: any;

  const planFree = { id: 'plan-free', code: 'free', name: 'Free' };
  const planPremium = { id: 'plan-premium', code: 'premium', name: 'Premium' };

  beforeEach(async () => {
    prisma = {
      userMembership: { findMany: jest.fn().mockResolvedValue([]) },
      membershipPlan: { findUnique: jest.fn() },
      user: { findUnique: jest.fn().mockResolvedValue({ appAccountToken: 'uuid-001' }) },
    };
    const module: TestingModule = await Test.createTestingModule({
      providers: [EffectiveMembershipService, { provide: PrismaService, useValue: prisma }],
    }).compile();
    service = module.get(EffectiveMembershipService);
  });

  it('Free user → effectivePlan=free', async () => {
    prisma.userMembership.findMany.mockResolvedValue([]);
    const result = await service.compute('u1');
    expect(result.effectivePlan).toBe('free');
    expect(result.grants).toHaveLength(0);
  });

  it('Admin permanent → premium', async () => {
    prisma.userMembership.findMany.mockResolvedValue([{ id:'m1', source:'admin', status:'active', expiresAt:null, gracePeriodExpiresAt:null, autoRenewStatus:null, environment:null, startedAt:new Date(), plan:planPremium }]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('premium');
  });

  it('Apple active → premium', async () => {
    prisma.userMembership.findMany.mockResolvedValue([{ id:'m1', source:'apple', status:'active', expiresAt:new Date(Date.now()+86400000), gracePeriodExpiresAt:null, autoRenewStatus:'on', environment:'Production', startedAt:new Date(), plan:planPremium }]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('premium');
  });

  it('grace_period within window → premium', async () => {
    prisma.userMembership.findMany.mockResolvedValue([{ id:'m1', source:'apple', status:'grace_period', expiresAt:new Date(Date.now()-3600000), gracePeriodExpiresAt:new Date(Date.now()+86400000), autoRenewStatus:'off', environment:'Production', startedAt:new Date(), plan:planPremium }]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('premium');
  });

  it('expired → free', async () => {
    prisma.userMembership.findMany.mockResolvedValue([{ id:'m1', source:'apple', status:'expired', expiresAt:new Date(Date.now()-86400000), gracePeriodExpiresAt:null, autoRenewStatus:null, environment:'Production', startedAt:new Date(), plan:planPremium }]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('free');
  });

  it('revoked → free', async () => {
    prisma.userMembership.findMany.mockResolvedValue([{ id:'m1', source:'apple', status:'revoked', expiresAt:new Date(Date.now()+86400000), gracePeriodExpiresAt:null, autoRenewStatus:null, environment:'Production', startedAt:new Date(), plan:planPremium }]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('free');
  });

  it('Apple expired + Admin active → premium (Admin untouched)', async () => {
    prisma.userMembership.findMany.mockResolvedValue([
      { id:'m1', source:'apple', status:'expired', expiresAt:new Date(Date.now()-86400000), gracePeriodExpiresAt:null, autoRenewStatus:null, environment:'Production', startedAt:new Date(), plan:planPremium },
      { id:'m2', source:'admin', status:'active', expiresAt:null, gracePeriodExpiresAt:null, autoRenewStatus:null, environment:null, startedAt:new Date(), plan:planPremium },
    ]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('premium');
    expect(r.grants).toHaveLength(1); // only admin grant active
  });

  it('autoRenew off but not yet expired → still premium', async () => {
    prisma.userMembership.findMany.mockResolvedValue([{ id:'m1', source:'apple', status:'active', expiresAt:new Date(Date.now()+86400000), gracePeriodExpiresAt:null, autoRenewStatus:'off', environment:'Production', startedAt:new Date(), plan:planPremium }]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('premium');
  });

  it('billing_retry with valid grace → premium', async () => {
    prisma.userMembership.findMany.mockResolvedValue([{ id:'m1', source:'apple', status:'billing_retry', expiresAt:new Date(Date.now()+86400000), gracePeriodExpiresAt:new Date(Date.now()+86400000), autoRenewStatus:'off', environment:'Production', startedAt:new Date(), plan:planPremium }]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('premium');
  });

  it('billing_retry with expired grace → free', async () => {
    prisma.userMembership.findMany.mockResolvedValue([{ id:'m1', source:'apple', status:'billing_retry', expiresAt:new Date(Date.now()-3600000), gracePeriodExpiresAt:new Date(Date.now()-3600000), autoRenewStatus:'off', environment:'Production', startedAt:new Date(), plan:planPremium }]);
    const r = await service.compute('u1');
    expect(r.effectivePlan).toBe('free');
  });

  it('appAccountToken returned', async () => {
    const r = await service.compute('u1');
    expect(r.appAccountToken).toBe('uuid-001');
  });
});
