import { Test, TestingModule } from '@nestjs/testing';
import { OrdersService } from './orders.service';
import { PrismaService } from '../../../prisma/prisma.service';
import { ConnectorFactory } from '../../connectors/connector.factory';
import { PaymentFactory } from '../../payments/payment.factory';
import { PaymentPollingService } from '../../payments/payment-polling.service';
import { BusinessService } from '../../business/business.service';
import { CartsService } from './carts.service';
import { OrderEventsService } from './order-events.service';
import { BadRequestException } from '@nestjs/common';

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

  const mockPrisma = {
    order: {
      findMany: jest.fn(),
      findUnique: jest.fn(),
      findFirst: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
    },
    cart: {
      findFirst: jest.fn(),
    },
    customer: {
      findFirst: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      findMany: jest.fn(),
    },
    business: {
      findFirst: jest.fn().mockResolvedValue(null),
      findMany: jest.fn().mockResolvedValue([]),
    },
    businessIntegration: {
      findFirst: jest.fn(),
    },
    payment: {
      create: jest.fn(),
      findUnique: jest.fn(),
      update: jest.fn(),
    },
    product: {
      findUnique: jest.fn(),
      update: jest.fn(),
    },
    productVariant: {
      findUnique: jest.fn(),
      update: jest.fn(),
    },
    session: {
      findFirst: jest.fn(),
      update: jest.fn(),
    },
  };

  const mockConnectorFactory = {
    getConnector: jest.fn().mockReturnValue({
      createOrder: jest.fn().mockResolvedValue({ externalOrderId: 'ext-ord-123' }),
      updateOrderStatus: jest.fn().mockResolvedValue(true),
      updatePaymentStatus: jest.fn().mockResolvedValue(true),
    }),
  };

  const mockPaymentGateway = {
    createPaymentLink: jest.fn().mockResolvedValue({
      paymentLink: 'https://pay.mock/123',
      paymentReference: 'PAY-123',
      gatewayReference: 'GW-123',
    }),
    verifyPayment: jest.fn().mockResolvedValue({
      status: 'SUCCESS',
      gatewayReference: 'GW-123',
    }),
  };

  const mockPaymentFactory = {
    getGateway: jest.fn().mockReturnValue(mockPaymentGateway),
  };

  const mockPaymentPolling = {
    startPolling: jest.fn(),
    notifyOwner: jest.fn().mockResolvedValue(true),
    notify: jest.fn().mockResolvedValue(true),
  };

  const mockBusinessService = {
    getDecryptedCredentials: jest.fn().mockResolvedValue({}),
  };

  const mockCartsService = {
    clearCart: jest.fn().mockResolvedValue(true),
  };

  const mockOrderEvents = {
    emitOrderUpdate: jest.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        OrdersService,
        { provide: PrismaService, useValue: mockPrisma },
        { provide: ConnectorFactory, useValue: mockConnectorFactory },
        { provide: PaymentFactory, useValue: mockPaymentFactory },
        { provide: PaymentPollingService, useValue: mockPaymentPolling },
        { provide: BusinessService, useValue: mockBusinessService },
        { provide: CartsService, useValue: mockCartsService },
        { provide: OrderEventsService, useValue: mockOrderEvents },
      ],
    }).compile();

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

  describe('listOrders', () => {
    it('should return list of orders for a business', async () => {
      mockPrisma.order.findMany.mockResolvedValueOnce([
        { id: 'ord-1', orderNumber: 'ORD-100', totalAmount: 50, currency: 'USD' },
      ]);

      const orders = await service.listOrders('biz-1');
      expect(orders).toHaveLength(1);
      expect(orders[0].orderNumber).toBe('ORD-100');
    });
  });

  describe('createOrderFromCart', () => {
    it('should throw BadRequestException if cart is empty', async () => {
      mockPrisma.business.findFirst.mockResolvedValueOnce(null);
      mockPrisma.business.findMany.mockResolvedValueOnce([]);
      mockPrisma.cart.findFirst.mockResolvedValueOnce(null);

      await expect(
        service.createOrderFromCart('biz-1', '263770000000', 'John Doe', 'delivery', '123 Main St'),
      ).rejects.toThrow(BadRequestException);
    });

    it('should create order and generate payment link successfully', async () => {
      mockPrisma.business.findFirst.mockResolvedValueOnce(null);
      mockPrisma.business.findMany.mockResolvedValueOnce([]);
      mockPrisma.cart.findFirst.mockResolvedValueOnce({
        id: 'cart-1',
        totalAmount: 100,
        currency: 'USD',
        items: [
          {
            productId: 'p1',
            quantity: 2,
            unitPrice: 50,
            lineTotal: 100,
            product: { name: 'Item 1', externalProductId: 'ext-p1' },
            variant: null,
          },
        ],
      });

      mockPrisma.customer.findFirst.mockResolvedValueOnce({
        id: 'cust-1',
        name: 'John Doe',
        whatsappNumber: '263770000000',
      });

      mockPrisma.order.create.mockResolvedValueOnce({
        id: 'ord-created-1',
        orderNumber: 'ORD-999999',
        totalAmount: 100,
        currency: 'USD',
        status: 'PENDING',
        customerPhone: '263770000000',
        customerName: 'John Doe',
        items: [],
      });

      mockPrisma.businessIntegration.findFirst.mockResolvedValue(null);

      const result = await service.createOrderFromCart(
        'biz-1',
        '263770000000',
        'John Doe',
        'delivery',
        '123 Street',
      );

      expect(result.orderId).toBe('ord-created-1');
      expect(result.paymentLink).toBe('https://pay.mock/123');
      expect(mockCartsService.clearCart).toHaveBeenCalledWith('cart-1');
      expect(mockOrderEvents.emitOrderUpdate).toHaveBeenCalled();
    });
  });

  describe('confirmPayment', () => {
    it('should update payment and order status to SUCCESS / PAID', async () => {
      mockPrisma.payment.findUnique.mockResolvedValueOnce({
        id: 'pay-1',
        businessId: 'biz-1',
        orderId: 'ord-1',
        status: 'PENDING',
        gateway: 'mock',
        order: {
          id: 'ord-1',
          orderNumber: 'ORD-123456',
          totalAmount: 50,
          currency: 'USD',
          customerPhone: '26377111222',
          customerName: 'Alice',
          deliveryMethod: 'delivery',
          items: [
            {
              id: 'oi-1',
              productId: 'p1',
              variantId: null,
              productName: 'Sample Product',
              quantity: 1,
              unitPrice: 50,
              lineTotal: 50,
            },
          ],
        },
      });

      mockPrisma.product.findUnique.mockResolvedValueOnce({
        id: 'p1',
        stockQuantity: 10,
      });

      mockPrisma.order.update.mockResolvedValueOnce({
        id: 'ord-1',
        orderNumber: 'ORD-123456',
        status: 'PROCESSING',
        paymentStatus: 'PAID',
        totalAmount: 50,
        currency: 'USD',
        customerPhone: '26377111222',
        customerName: 'Alice',
      });

      const updated = await service.confirmPayment('PAY-123', 'SUCCESS', 'GW-REF-999');

      expect(updated.status).toBe('PROCESSING');
      expect(mockPrisma.payment.update).toHaveBeenCalled();
      expect(mockPrisma.product.update).toHaveBeenCalled();
      expect(mockPaymentPolling.notify).toHaveBeenCalled();
    });
  });
});
