import { SubscriptionEntity } from '@modules/subscriptions/domain/entities/subscription.entity';
import { type ISubscriptionRepository } from '@modules/subscriptions/domain/repositories/subscription.repository';
import { AdjustSubscriptionCommand } from '../commands/adjust-subscription/adjust-subscription.command';
import { AdjustSubscriptionHandler } from '../commands/adjust-subscription/adjust-subscription.handler';

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

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

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

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

    handler = new AdjustSubscriptionHandler(
      mockSubRepo as any,
      mockPrisma,
      mockEventBus as any,
    );
  });

  it('adjusts subscription to a new plan successfully', async () => {
    const sub = SubscriptionEntity.createNew(
      'sub-1', 'user-1', 'plan-free', 'FREE',
      new Date(), new Date(Date.now() + 30 * 86400000),
    );
    sub.clearDomainEvents();

    mockSubRepo.findByUserId.mockResolvedValue(sub);
    mockPrisma.plan.findUnique.mockResolvedValue({
      id: 'plan-pro',
      tier: 'AGENT_PRO',
    });
    mockSubRepo.update.mockResolvedValue(undefined);

    const command = new AdjustSubscriptionCommand('user-1', 'admin-1', 'AGENT_PRO', 'Courtesy upgrade');
    const result = await handler.execute(command);

    expect(result.newPlanTier).toBe('AGENT_PRO');
    expect(mockSubRepo.update).toHaveBeenCalledTimes(1);
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

  it('throws NotFoundException when user has no subscription', async () => {
    mockSubRepo.findByUserId.mockResolvedValue(null);

    const command = new AdjustSubscriptionCommand('user-1', 'admin-1', 'AGENT_PRO', 'test');

    await expect(handler.execute(command)).rejects.toThrow('Người dùng chưa có subscription');
  });

  it('throws ValidationException for invalid plan tier', async () => {
    const command = new AdjustSubscriptionCommand('user-1', 'admin-1', 'INVALID_TIER', 'test');

    await expect(handler.execute(command)).rejects.toThrow(/không hợp lệ/);
  });

  it('throws ValidationException when user is already on the same plan', async () => {
    const sub = SubscriptionEntity.createNew(
      'sub-1', 'user-1', 'plan-pro', 'AGENT_PRO',
      new Date(), new Date(Date.now() + 30 * 86400000),
    );
    sub.clearDomainEvents();

    mockSubRepo.findByUserId.mockResolvedValue(sub);
    mockPrisma.plan.findUnique.mockResolvedValue({
      id: 'plan-pro',
      tier: 'AGENT_PRO',
    });

    const command = new AdjustSubscriptionCommand('user-1', 'admin-1', 'AGENT_PRO', 'test');

    await expect(handler.execute(command)).rejects.toThrow(/đã đang sử dụng/);
  });
});
