import { SubscriptionEntity } from '../../domain/entities/subscription.entity';
import { type ISubscriptionRepository } from '../../domain/repositories/subscription.repository';
import { CancelSubscriptionCommand } from '../commands/cancel-subscription/cancel-subscription.command';
import { CancelSubscriptionHandler } from '../commands/cancel-subscription/cancel-subscription.handler';

function createActiveSubscription(userId = 'user-1'): SubscriptionEntity {
  const sub = SubscriptionEntity.createNew(
    'sub-1', userId, 'plan-1', 'AGENT_PRO',
    new Date('2026-01-01'), new Date('2026-02-01'),
  );
  sub.clearDomainEvents();
  return sub;
}

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

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

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

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

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

  it('cancels an active subscription', async () => {
    const subscription = createActiveSubscription();
    mockRepo.findByUserId.mockResolvedValue(subscription);

    const command = new CancelSubscriptionCommand('user-1', 'Không cần nữa');
    const result = await handler.execute(command);

    expect(result.status).toBe('CANCELLED');
    expect(result.subscriptionId).toBe('sub-1');
    expect(result.cancelledAt).toBeDefined();
    expect(mockRepo.update).toHaveBeenCalledTimes(1);
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

  it('throws NotFoundException when no subscription found', async () => {
    mockRepo.findByUserId.mockResolvedValue(null);

    const command = new CancelSubscriptionCommand('user-99');

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

  it('throws ValidationException when already cancelled', async () => {
    const subscription = createActiveSubscription();
    subscription.cancel();
    subscription.clearDomainEvents();
    mockRepo.findByUserId.mockResolvedValue(subscription);

    const command = new CancelSubscriptionCommand('user-1');

    await expect(handler.execute(command)).rejects.toThrow(/hủy/);
  });
});
