import { Test, TestingModule } from '@nestjs/testing';
import { OrdersService } from '../orders.service';
import { PrismaService } from '../../../prisma/prisma.service';
import { GatewayPayService } from '../../payment/gateway-pay.service';

describe('OrdersService', () => {
  let service: OrdersService;
  let prisma: PrismaService;
  let gatewayPay: GatewayPayService;

  const mockPrisma = {
    order: {
      create: jest.fn(),
      findMany: jest.fn(),
      findUnique: jest.fn(),
      update: jest.fn(),
      count: jest.fn(),
    },
    subscription: {
      findFirst: jest.fn(),
      findMany: jest.fn(),
      create: jest.fn(),
    },
    user: {
      update: jest.fn(),
    },
  };

  const mockGatewayPay = {
    createOrder: jest.fn(),
    queryOrder: jest.fn(),
    refund: jest.fn(),
    handleWebhook: jest.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        OrdersService,
        { provide: PrismaService, useValue: mockPrisma },
        { provide: GatewayPayService, useValue: mockGatewayPay },
      ],
    }).compile();

    service = module.get<OrdersService>(OrdersService);
    prisma = module.get<PrismaService>(PrismaService);
    gatewayPay = module.get<GatewayPayService>(GatewayPayService);
    jest.clearAllMocks();
  });

  describe('create', () => {
    it('should create order and call gateway for alipay', async () => {
      const mockOrder = { 
        id: 1, 
        orderNo: 'YZR123',
        amount: 49.9,
        planType: 'MONTHLY',
        payChannel: 'alipay',
      };
      mockPrisma.order.create.mockResolvedValue(mockOrder);
      mockPrisma.order.update.mockResolvedValue(mockOrder);
      mockGatewayPay.createOrder.mockResolvedValue({
        gatewayOrderId: 'gateway_abc',
        payUrl: 'https://pay.example.com',
        status: 'pending',
      });

      const result = await service.create(1, {
        amount: 49.9,
        planType: 'MONTHLY',
        payChannel: 'alipay',
      });

      expect(result).toHaveProperty('order');
      expect(result).toHaveProperty('payResult');
      expect(mockGatewayPay.createOrder).toHaveBeenCalledWith(
        expect.objectContaining({
          paymentMethod: 'alipay',
          amount: 49.9,
        })
      );
    });

    it('should create order and call gateway for wechat', async () => {
      const mockOrder = { 
        id: 2, 
        orderNo: 'YZR456',
        amount: 299,
        planType: 'YEARLY',
        payChannel: 'wxpay',
      };
      mockPrisma.order.create.mockResolvedValue(mockOrder);
      mockPrisma.order.update.mockResolvedValue(mockOrder);
      mockGatewayPay.createOrder.mockResolvedValue({
        gatewayOrderId: 'gateway_def',
        qrcode: 'data:image/png;base64,...',
        status: 'pending',
      });

      const result = await service.create(1, {
        amount: 299,
        planType: 'YEARLY',
        payChannel: 'wxpay',
      });

      expect(result).toHaveProperty('order');
      expect(result).toHaveProperty('payResult');
      expect(mockGatewayPay.createOrder).toHaveBeenCalledWith(
        expect.objectContaining({
          paymentMethod: 'wechat',
          amount: 299,
        })
      );
    });
  });

  describe('findByUser', () => {
    it('should return paginated orders for user', async () => {
      const mockOrders = [
        { id: 1, orderNo: 'YZR123', amount: 49.9 }
      ];
      mockPrisma.order.findMany.mockResolvedValue(mockOrders);
      mockPrisma.order.count.mockResolvedValue(1);

      const result = await service.findByUser(1, { page: 1, pageSize: 20 });

      expect(result).toEqual({
        items: mockOrders,
        total: 1,
        page: 1,
        pageSize: 20,
      });
    });
  });

  describe('findByOrderNo', () => {
    it('should return order by orderNo', async () => {
      const mockOrder = { id: 1, orderNo: 'YZR123' };
      mockPrisma.order.findUnique.mockResolvedValue(mockOrder);

      const result = await service.findByOrderNo('YZR123');

      expect(result).toEqual(mockOrder);
      expect(mockPrisma.order.findUnique).toHaveBeenCalledWith({
        where: { orderNo: 'YZR123' },
      });
    });
  });

  describe('getCurrentSubscription', () => {
    it('should return active subscription', async () => {
      const mockSub = { 
        id: 1, 
        userId: 1, 
        plan: 'YEARLY',
        status: 'ACTIVE',
        endDate: new Date(Date.now() + 86400000),
      };
      mockPrisma.subscription.findFirst.mockResolvedValue(mockSub);

      const result = await service.getCurrentSubscription(1);

      expect(result).toEqual(mockSub);
    });

    it('should return null if no active subscription', async () => {
      mockPrisma.subscription.findFirst.mockResolvedValue(null);

      const result = await service.getCurrentSubscription(1);

      expect(result).toBeNull();
    });
  });

  describe('getSubscriptions', () => {
    it('should return all subscriptions for user', async () => {
      const mockSubs = [
        { id: 1, plan: 'MONTHLY' },
        { id: 2, plan: 'YEARLY' },
      ];
      mockPrisma.subscription.findMany.mockResolvedValue(mockSubs);

      const result = await service.getSubscriptions(1);

      expect(result).toEqual(mockSubs);
    });
  });
});
