import { ConflictException, NotFoundException } from '@modules/shared/domain/domain-exception';
import { SubscriptionEntity } from '../../domain/entities/subscription.entity';
import { type ISubscriptionRepository } from '../../domain/repositories/subscription.repository';
import { CreateSubscriptionCommand } from '../commands/create-subscription/create-subscription.command';
import { CreateSubscriptionHandler } from '../commands/create-subscription/create-subscription.handler';

describe('CreateSubscriptionHandler', () => {
  let handler: CreateSubscriptionHandler;
  let mockRepo: { [K in keyof ISubscriptionRepository]: ReturnType<typeof vi.fn> };
  let mockPrisma: any;
  let mockEventBus: { publish: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockRepo = {
      findById: vi.fn(),
      findByUserId: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
    };

    mockPrisma = {
      plan: {
        findFirst: vi.fn(),
      },
    };

    mockEventBus = {
      publish: vi.fn(),
    };

    const mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), verbose: vi.fn() };

    handler = new CreateSubscriptionHandler(
      mockRepo as any,
      mockPrisma,
      mockEventBus as any,
      mockLogger as any,
    );
  });

  it('creates subscription successfully', async () => {
    mockRepo.findByUserId.mockResolvedValue(null);
    mockPrisma.plan.findFirst.mockResolvedValue({
      id: 'plan-1',
      tier: 'AGENT_PRO',
      isActive: true,
    });
    mockRepo.save.mockResolvedValue(undefined);

    const command = new CreateSubscriptionCommand('user-1', 'AGENT_PRO', 'monthly');
    const result = await handler.execute(command);

    expect(result.planTier).toBe('AGENT_PRO');
    expect(result.status).toBe('ACTIVE');
    expect(result.subscriptionId).toBeDefined();
    expect(mockRepo.save).toHaveBeenCalledTimes(1);
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

  it('throws ConflictException when user already has active subscription', async () => {
    const existing = SubscriptionEntity.createNew(
      'sub-1', 'user-1', 'plan-1', 'FREE',
      new Date(), new Date(),
    );
    mockRepo.findByUserId.mockResolvedValue(existing);

    const command = new CreateSubscriptionCommand('user-1', 'AGENT_PRO', 'monthly');

    await expect(handler.execute(command)).rejects.toThrow(ConflictException);
  });

  it('throws NotFoundException when plan does not exist', async () => {
    mockRepo.findByUserId.mockResolvedValue(null);
    mockPrisma.plan.findFirst.mockResolvedValue(null);

    const command = new CreateSubscriptionCommand('user-1', 'AGENT_PRO', 'monthly');

    await expect(handler.execute(command)).rejects.toThrow(NotFoundException);
  });

  it('creates yearly subscription with correct period', async () => {
    mockRepo.findByUserId.mockResolvedValue(null);
    mockPrisma.plan.findFirst.mockResolvedValue({
      id: 'plan-1',
      tier: 'INVESTOR',
      isActive: true,
    });
    mockRepo.save.mockResolvedValue(undefined);

    const command = new CreateSubscriptionCommand('user-1', 'INVESTOR', 'yearly');
    const result = await handler.execute(command);

    const startYear = result.currentPeriodStart.getFullYear();
    const endYear = result.currentPeriodEnd.getFullYear();
    expect(endYear - startYear).toBe(1);
  });
});
