import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe, ExecutionContext } from '@nestjs/common';
import * as request from 'supertest';
import { HealthController } from '../src/api/controllers/health.controller';
import { PlantingOrderController } from '../src/api/controllers/planting-order.controller';
import { PlantingPositionController } from '../src/api/controllers/planting-position.controller';
import { PlantingApplicationService } from '../src/application/services/planting-application.service';
import { JwtAuthGuard } from '../src/api/guards/jwt-auth.guard';
import { ConfigModule } from '@nestjs/config';

// Mock for PlantingApplicationService
const mockPlantingService = {
  createOrder: jest.fn(),
  selectProvinceCity: jest.fn(),
  confirmProvinceCity: jest.fn(),
  payOrder: jest.fn(),
  getUserOrders: jest.fn(),
  getOrderDetail: jest.fn(),
  getUserPosition: jest.fn(),
  cancelOrder: jest.fn(),
};

// Mock JwtAuthGuard that always rejects unauthorized requests
const mockJwtAuthGuardReject = {
  canActivate: jest.fn().mockReturnValue(false),
};

describe('PlantingController (e2e) - Unauthorized', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot({
          isGlobal: true,
          envFilePath: '.env.test',
        }),
      ],
      controllers: [HealthController, PlantingOrderController, PlantingPositionController],
      providers: [
        {
          provide: PlantingApplicationService,
          useValue: mockPlantingService,
        },
      ],
    })
      .overrideGuard(JwtAuthGuard)
      .useValue(mockJwtAuthGuardReject)
      .compile();

    app = moduleFixture.createNestApplication();
    app.setGlobalPrefix('api/v1');
    app.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        forbidNonWhitelisted: true,
        transform: true,
      }),
    );
    await app.init();
  });

  afterAll(async () => {
    await app.close();
  });

  describe('/health (GET)', () => {
    it('应该返回健康状态', () => {
      return request(app.getHttpServer())
        .get('/api/v1/health')
        .expect(200)
        .expect((res) => {
          expect(res.body.status).toBe('ok');
          expect(res.body.service).toBe('planting-service');
        });
    });
  });

  describe('/health/ready (GET)', () => {
    it('应该返回就绪状态', () => {
      return request(app.getHttpServer())
        .get('/api/v1/health/ready')
        .expect(200)
        .expect((res) => {
          expect(res.body.status).toBe('ready');
        });
    });
  });

  describe('/planting/orders (POST)', () => {
    it('应该拒绝未认证的请求', () => {
      return request(app.getHttpServer())
        .post('/api/v1/planting/orders')
        .send({ treeCount: 1 })
        .expect(403);
    });
  });

  describe('/planting/orders (GET)', () => {
    it('应该拒绝未认证的请求', () => {
      return request(app.getHttpServer())
        .get('/api/v1/planting/orders')
        .expect(403);
    });
  });

  describe('/planting/position (GET)', () => {
    it('应该拒绝未认证的请求', () => {
      return request(app.getHttpServer())
        .get('/api/v1/planting/position')
        .expect(403);
    });
  });
});

describe('PlantingController (e2e) - Authorized', () => {
  let app: INestApplication;
  const mockUser = { id: '1', username: 'testuser' };

  beforeAll(async () => {
    // Reset mocks
    jest.clearAllMocks();

    const mockJwtAuthGuardAccept = {
      canActivate: (context: ExecutionContext) => {
        const req = context.switchToHttp().getRequest();
        req.user = mockUser;
        return true;
      },
    };

    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot({
          isGlobal: true,
          envFilePath: '.env.test',
        }),
      ],
      controllers: [HealthController, PlantingOrderController, PlantingPositionController],
      providers: [
        {
          provide: PlantingApplicationService,
          useValue: mockPlantingService,
        },
      ],
    })
      .overrideGuard(JwtAuthGuard)
      .useValue(mockJwtAuthGuardAccept)
      .compile();

    app = moduleFixture.createNestApplication();
    app.setGlobalPrefix('api/v1');
    app.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        forbidNonWhitelisted: true,
        transform: true,
      }),
    );
    await app.init();
  });

  afterAll(async () => {
    await app.close();
  });

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('/planting/orders (POST)', () => {
    it('应该成功创建订单', async () => {
      const mockOrder = {
        orderNo: 'PO202411300001',
        userId: '1',
        treeCount: 5,
        totalAmount: 10995,
        status: 'CREATED',
        createdAt: new Date().toISOString(),
      };

      mockPlantingService.createOrder.mockResolvedValue(mockOrder);

      const response = await request(app.getHttpServer())
        .post('/api/v1/planting/orders')
        .send({ treeCount: 5 })
        .expect(201);

      expect(response.body.orderNo).toBe('PO202411300001');
      expect(response.body.treeCount).toBe(5);
    });

    it('应该验证treeCount必须为正整数', async () => {
      await request(app.getHttpServer())
        .post('/api/v1/planting/orders')
        .send({ treeCount: 0 })
        .expect(400);

      await request(app.getHttpServer())
        .post('/api/v1/planting/orders')
        .send({ treeCount: -1 })
        .expect(400);
    });

    it('应该验证treeCount不能超过最大限制', async () => {
      await request(app.getHttpServer())
        .post('/api/v1/planting/orders')
        .send({ treeCount: 1001 })
        .expect(400);
    });
  });

  describe('/planting/orders (GET)', () => {
    it('应该返回用户订单列表', async () => {
      const mockOrders = [
        {
          orderNo: 'PO202411300001',
          treeCount: 5,
          totalAmount: 10995,
          status: 'CREATED',
        },
        {
          orderNo: 'PO202411300002',
          treeCount: 3,
          totalAmount: 6597,
          status: 'PAID',
        },
      ];

      mockPlantingService.getUserOrders.mockResolvedValue(mockOrders);

      const response = await request(app.getHttpServer())
        .get('/api/v1/planting/orders')
        .query({ page: 1, pageSize: 10 })
        .expect(200);

      expect(response.body).toHaveLength(2);
    });
  });

  describe('/planting/orders/:orderNo (GET)', () => {
    it('应该返回订单详情', async () => {
      const mockOrder = {
        orderNo: 'PO202411300001',
        treeCount: 5,
        totalAmount: 10995,
        status: 'CREATED',
      };

      mockPlantingService.getOrderDetail.mockResolvedValue(mockOrder);

      const response = await request(app.getHttpServer())
        .get('/api/v1/planting/orders/PO202411300001')
        .expect(200);

      expect(response.body.orderNo).toBe('PO202411300001');
    });

    it('应该返回404当订单不存在', async () => {
      mockPlantingService.getOrderDetail.mockResolvedValue(null);

      await request(app.getHttpServer())
        .get('/api/v1/planting/orders/NONEXISTENT')
        .expect(404);
    });
  });

  describe('/planting/position (GET)', () => {
    it('应该返回用户持仓信息', async () => {
      const mockPosition = {
        totalTreeCount: 10,
        effectiveTreeCount: 8,
        pendingTreeCount: 2,
        distributions: [
          { provinceCode: '440000', cityCode: '440100', treeCount: 10 },
        ],
      };

      mockPlantingService.getUserPosition.mockResolvedValue(mockPosition);

      const response = await request(app.getHttpServer())
        .get('/api/v1/planting/position')
        .expect(200);

      expect(response.body.totalTreeCount).toBe(10);
      expect(response.body.distributions).toHaveLength(1);
    });
  });

  describe('/planting/orders/:orderNo/select-province-city (POST)', () => {
    it('应该成功选择省市', async () => {
      mockPlantingService.selectProvinceCity.mockResolvedValue({
        success: true,
        message: '省市选择成功',
      });

      const response = await request(app.getHttpServer())
        .post('/api/v1/planting/orders/PO202411300001/select-province-city')
        .send({
          provinceCode: '440000',
          provinceName: '广东省',
          cityCode: '440100',
          cityName: '广州市',
        })
        .expect(200);

      expect(response.body.success).toBe(true);
    });

    it('应该验证省市参数', async () => {
      await request(app.getHttpServer())
        .post('/api/v1/planting/orders/PO202411300001/select-province-city')
        .send({
          provinceCode: '',
          provinceName: '广东省',
        })
        .expect(400);
    });
  });

  describe('/planting/orders/:orderNo/confirm-province-city (POST)', () => {
    it('应该成功确认省市', async () => {
      mockPlantingService.confirmProvinceCity.mockResolvedValue({
        success: true,
        message: '省市确认成功',
      });

      const response = await request(app.getHttpServer())
        .post('/api/v1/planting/orders/PO202411300001/confirm-province-city')
        .expect(200);

      expect(response.body.success).toBe(true);
    });
  });

  describe('/planting/orders/:orderNo/pay (POST)', () => {
    it('应该成功支付订单', async () => {
      const mockPayResult = {
        orderNo: 'PO202411300001',
        status: 'POOL_SCHEDULED',
        allocations: [
          { targetType: 'POOL', amount: 1979.1 },
          { targetType: 'OPERATION', amount: 109.95 },
        ],
      };

      mockPlantingService.payOrder.mockResolvedValue(mockPayResult);

      const response = await request(app.getHttpServer())
        .post('/api/v1/planting/orders/PO202411300001/pay')
        .expect(200);

      expect(response.body.status).toBe('POOL_SCHEDULED');
      expect(response.body.allocations).toHaveLength(2);
    });
  });

  describe('/planting/orders/:orderNo/cancel (POST)', () => {
    it('应该成功取消订单', async () => {
      mockPlantingService.cancelOrder.mockResolvedValue({
        success: true,
        message: '订单取消成功',
      });

      const response = await request(app.getHttpServer())
        .post('/api/v1/planting/orders/PO202411300001/cancel')
        .expect(200);

      expect(response.body.success).toBe(true);
    });
  });
});
