import { Test, TestingModule } from '@nestjs/testing';
import { PlantingApplicationService } from './planting-application.service';
import { FundAllocationDomainService } from '../../domain/services/fund-allocation.service';
import {
  IPlantingOrderRepository,
  PLANTING_ORDER_REPOSITORY,
} from '../../domain/repositories/planting-order.repository.interface';
import {
  IPlantingPositionRepository,
  PLANTING_POSITION_REPOSITORY,
} from '../../domain/repositories/planting-position.repository.interface';
import {
  IPoolInjectionBatchRepository,
  POOL_INJECTION_BATCH_REPOSITORY,
} from '../../domain/repositories/pool-injection-batch.repository.interface';
import { WalletServiceClient } from '../../infrastructure/external/wallet-service.client';
import { ReferralServiceClient } from '../../infrastructure/external/referral-service.client';
import { PlantingOrder } from '../../domain/aggregates/planting-order.aggregate';
import { PlantingPosition } from '../../domain/aggregates/planting-position.aggregate';
import { PoolInjectionBatch } from '../../domain/aggregates/pool-injection-batch.aggregate';
import { PlantingOrderStatus } from '../../domain/value-objects/planting-order-status.enum';

describe('PlantingApplicationService (Integration)', () => {
  let service: PlantingApplicationService;
  let orderRepository: jest.Mocked<IPlantingOrderRepository>;
  let positionRepository: jest.Mocked<IPlantingPositionRepository>;
  let batchRepository: jest.Mocked<IPoolInjectionBatchRepository>;
  let walletService: jest.Mocked<WalletServiceClient>;
  let referralService: jest.Mocked<ReferralServiceClient>;

  beforeEach(async () => {
    // Create mocks
    orderRepository = {
      save: jest.fn(),
      findById: jest.fn(),
      findByOrderNo: jest.fn(),
      findByUserId: jest.fn(),
      findByStatus: jest.fn(),
      findPendingPoolScheduling: jest.fn(),
      findByBatchId: jest.fn(),
      findReadyForMining: jest.fn(),
      countTreesByUserId: jest.fn(),
      countByUserId: jest.fn(),
      findPaidOrdersWithoutContract: jest.fn(),
    };

    positionRepository = {
      save: jest.fn(),
      findById: jest.fn(),
      findByUserId: jest.fn(),
      getOrCreate: jest.fn(),
    };

    batchRepository = {
      save: jest.fn(),
      findById: jest.fn(),
      findByBatchNo: jest.fn(),
      findByStatus: jest.fn(),
      findCurrentBatch: jest.fn(),
      findOrCreateCurrentBatch: jest.fn(),
      findScheduledBatchesReadyForInjection: jest.fn(),
    };

    walletService = {
      getBalance: jest.fn(),
      deductForPlanting: jest.fn(),
      allocateFunds: jest.fn(),
      injectToPool: jest.fn(),
    } as any;

    referralService = {
      getReferralContext: jest.fn(),
    } as any;

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        PlantingApplicationService,
        FundAllocationDomainService,
        { provide: PLANTING_ORDER_REPOSITORY, useValue: orderRepository },
        { provide: PLANTING_POSITION_REPOSITORY, useValue: positionRepository },
        { provide: POOL_INJECTION_BATCH_REPOSITORY, useValue: batchRepository },
        { provide: WalletServiceClient, useValue: walletService },
        { provide: ReferralServiceClient, useValue: referralService },
      ],
    }).compile();

    service = module.get<PlantingApplicationService>(PlantingApplicationService);
  });

  describe('createOrder', () => {
    it('应该成功创建订单', async () => {
      const userId = BigInt(1);
      const treeCount = 5;

      orderRepository.countTreesByUserId.mockResolvedValue(0);
      walletService.getBalance.mockResolvedValue({
        userId: '1',
        available: 100000,
        locked: 0,
        currency: 'USDT',
      });
      orderRepository.save.mockResolvedValue(undefined);

      const result = await service.createOrder(userId, treeCount);

      expect(result.treeCount).toBe(5);
      expect(result.totalAmount).toBe(5 * 2199);
      expect(result.status).toBe(PlantingOrderStatus.CREATED);
      expect(orderRepository.save).toHaveBeenCalled();
    });

    it('应该拒绝超过限购数量', async () => {
      const userId = BigInt(1);
      const treeCount = 100;

      orderRepository.countTreesByUserId.mockResolvedValue(950);

      await expect(service.createOrder(userId, treeCount)).rejects.toThrow(
        '超过个人最大认种数量限制',
      );
    });

    it('应该拒绝余额不足', async () => {
      const userId = BigInt(1);
      const treeCount = 5;

      orderRepository.countTreesByUserId.mockResolvedValue(0);
      walletService.getBalance.mockResolvedValue({
        userId: '1',
        available: 100,
        locked: 0,
        currency: 'USDT',
      });

      await expect(service.createOrder(userId, treeCount)).rejects.toThrow(
        '余额不足',
      );
    });
  });

  describe('selectProvinceCity', () => {
    it('应该成功选择省市', async () => {
      const order = PlantingOrder.create(BigInt(1), 1);
      orderRepository.findByOrderNo.mockResolvedValue(order);
      orderRepository.save.mockResolvedValue(undefined);

      const result = await service.selectProvinceCity(
        order.orderNo,
        BigInt(1),
        '440000',
        '广东省',
        '440100',
        '广州市',
      );

      expect(result.success).toBe(true);
      expect(orderRepository.save).toHaveBeenCalled();
    });

    it('应该拒绝不存在的订单', async () => {
      orderRepository.findByOrderNo.mockResolvedValue(null);

      await expect(
        service.selectProvinceCity(
          'invalid',
          BigInt(1),
          '440000',
          '广东省',
          '440100',
          '广州市',
        ),
      ).rejects.toThrow('订单不存在');
    });

    it('应该拒绝无权操作的订单', async () => {
      const order = PlantingOrder.create(BigInt(1), 1);
      orderRepository.findByOrderNo.mockResolvedValue(order);

      await expect(
        service.selectProvinceCity(
          order.orderNo,
          BigInt(999),
          '440000',
          '广东省',
          '440100',
          '广州市',
        ),
      ).rejects.toThrow('无权操作此订单');
    });
  });

  describe('confirmProvinceCity', () => {
    it('应该在5秒后成功确认省市', async () => {
      jest.useFakeTimers();

      const order = PlantingOrder.create(BigInt(1), 1);
      order.selectProvinceCity('440000', '广东省', '440100', '广州市');

      jest.advanceTimersByTime(5000);

      orderRepository.findByOrderNo.mockResolvedValue(order);
      orderRepository.save.mockResolvedValue(undefined);

      const result = await service.confirmProvinceCity(order.orderNo, BigInt(1));

      expect(result.success).toBe(true);
      expect(orderRepository.save).toHaveBeenCalled();

      jest.useRealTimers();
    });
  });

  describe('payOrder', () => {
    it('应该成功支付订单并完成资金分配', async () => {
      jest.useFakeTimers();

      const order = PlantingOrder.create(BigInt(1), 1);
      order.selectProvinceCity('440000', '广东省', '440100', '广州市');
      jest.advanceTimersByTime(5000);
      order.confirmProvinceCity();

      const position = PlantingPosition.create(BigInt(1));
      const batch = PoolInjectionBatch.create(new Date());
      (batch as any)._id = BigInt(1);

      orderRepository.findByOrderNo.mockResolvedValue(order);
      walletService.deductForPlanting.mockResolvedValue(true);
      referralService.getReferralContext.mockResolvedValue({
        referralChain: [],
        nearestProvinceAuth: null,
        nearestCityAuth: null,
        nearestCommunity: null,
      });
      walletService.allocateFunds.mockResolvedValue(true);
      positionRepository.getOrCreate.mockResolvedValue(position);
      batchRepository.findOrCreateCurrentBatch.mockResolvedValue(batch);
      orderRepository.save.mockResolvedValue(undefined);
      positionRepository.save.mockResolvedValue(undefined);
      batchRepository.save.mockResolvedValue(undefined);

      const result = await service.payOrder(order.orderNo, BigInt(1));

      expect(result.status).toBe(PlantingOrderStatus.POOL_SCHEDULED);
      expect(result.allocations.length).toBe(10);
      expect(walletService.deductForPlanting).toHaveBeenCalled();
      expect(walletService.allocateFunds).toHaveBeenCalled();

      jest.useRealTimers();
    });
  });

  describe('getUserOrders', () => {
    it('应该返回用户订单列表', async () => {
      const order1 = PlantingOrder.create(BigInt(1), 1);
      const order2 = PlantingOrder.create(BigInt(1), 2);

      orderRepository.findByUserId.mockResolvedValue([order1, order2]);

      const result = await service.getUserOrders(BigInt(1), 1, 10);

      expect(result.length).toBe(2);
      expect(result[0].treeCount).toBe(1);
      expect(result[1].treeCount).toBe(2);
    });
  });

  describe('getUserPosition', () => {
    it('应该返回用户持仓信息', async () => {
      const position = PlantingPosition.create(BigInt(1));
      position.addPlanting(5, '440000', '440100');

      positionRepository.findByUserId.mockResolvedValue(position);

      const result = await service.getUserPosition(BigInt(1));

      expect(result.totalTreeCount).toBe(5);
      expect(result.pendingTreeCount).toBe(5);
      expect(result.distributions.length).toBe(1);
    });

    it('应该返回空持仓当用户无记录', async () => {
      positionRepository.findByUserId.mockResolvedValue(null);

      const result = await service.getUserPosition(BigInt(1));

      expect(result.totalTreeCount).toBe(0);
      expect(result.effectiveTreeCount).toBe(0);
      expect(result.pendingTreeCount).toBe(0);
    });
  });

  describe('cancelOrder', () => {
    it('应该成功取消未支付订单', async () => {
      const order = PlantingOrder.create(BigInt(1), 1);
      orderRepository.findByOrderNo.mockResolvedValue(order);
      orderRepository.save.mockResolvedValue(undefined);

      const result = await service.cancelOrder(order.orderNo, BigInt(1));

      expect(result.success).toBe(true);
      expect(orderRepository.save).toHaveBeenCalled();
    });
  });
});
