import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import request from 'supertest';
import { App } from 'supertest/types';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { HealthController } from '../src/api/controllers/health.controller';
import { RewardController } from '../src/api/controllers/reward.controller';
import { SettlementController } from '../src/api/controllers/settlement.controller';
import { RewardApplicationService } from '../src/application/services/reward-application.service';
import { JwtService } from '@nestjs/jwt';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from '../src/shared/strategies/jwt.strategy';
import { RewardStatus } from '../src/domain/value-objects/reward-status.enum';
import { RightType } from '../src/domain/value-objects/right-type.enum';

describe('Reward Service (e2e)', () => {
  let app: INestApplication<App>;
  let jwtService: JwtService;
  let mockRewardService: any;

  const TEST_JWT_SECRET = 'test-secret-key-for-testing';

  const createTestToken = (userId: string = '100') => {
    return jwtService.sign({
      sub: userId,
      username: 'testuser',
      roles: ['user'],
    });
  };

  beforeEach(async () => {
    mockRewardService = {
      getRewardSummary: jest.fn().mockResolvedValue({
        pendingUsdt: 1000,
        pendingHashpower: 0.5,
        pendingExpireAt: new Date(Date.now() + 12 * 60 * 60 * 1000),
        settleableUsdt: 500,
        settleableHashpower: 0.2,
        settledTotalUsdt: 2000,
        settledTotalHashpower: 1.0,
        expiredTotalUsdt: 100,
        expiredTotalHashpower: 0.1,
      }),
      getRewardDetails: jest.fn().mockResolvedValue({
        data: [
          {
            id: '1',
            rightType: RightType.SHARE_RIGHT,
            usdtAmount: 500,
            hashpowerAmount: 0,
            rewardStatus: RewardStatus.PENDING,
            createdAt: new Date(),
            expireAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
            remainingTimeMs: 86400000,
            claimedAt: null,
            settledAt: null,
            expiredAt: null,
            memo: 'Test reward',
          },
        ],
        pagination: {
          page: 1,
          pageSize: 20,
          total: 1,
        },
      }),
      getPendingRewards: jest.fn().mockResolvedValue([
        {
          id: '1',
          rightType: RightType.SHARE_RIGHT,
          usdtAmount: 500,
          hashpowerAmount: 0,
          createdAt: new Date(),
          expireAt: new Date(Date.now() + 12 * 60 * 60 * 1000),
          remainingTimeMs: 43200000,
          memo: 'Pending reward',
        },
      ]),
      settleRewards: jest.fn().mockResolvedValue({
        success: true,
        settledUsdtAmount: 500,
        receivedAmount: 0.25,
        settleCurrency: 'BNB',
        txHash: '0x123abc',
      }),
    };

    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot({
          isGlobal: true,
          load: [() => ({ JWT_SECRET: TEST_JWT_SECRET })],
        }),
        PassportModule,
        JwtModule.register({
          secret: TEST_JWT_SECRET,
          signOptions: { expiresIn: '1h' },
        }),
      ],
      controllers: [HealthController, RewardController, SettlementController],
      providers: [
        {
          provide: RewardApplicationService,
          useValue: mockRewardService,
        },
        {
          provide: JwtStrategy,
          useFactory: (configService: ConfigService) => {
            return new JwtStrategy({
              get: (key: string) => key === 'JWT_SECRET' ? TEST_JWT_SECRET : undefined,
            } as ConfigService);
          },
          inject: [ConfigService],
        },
      ],
    }).compile();

    app = moduleFixture.createNestApplication();
    app.useGlobalPipes(new ValidationPipe({ transform: true }));
    await app.init();

    jwtService = moduleFixture.get<JwtService>(JwtService);
  });

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

  describe('Health Check', () => {
    it('/health (GET) should return healthy status', () => {
      return request(app.getHttpServer())
        .get('/health')
        .expect(200)
        .expect((res) => {
          expect(res.body.status).toBe('ok');
          expect(res.body.service).toBe('reward-service');
          expect(res.body.timestamp).toBeDefined();
        });
    });
  });

  describe('Rewards API', () => {
    describe('GET /rewards/summary', () => {
      it('should return 401 without auth token', () => {
        return request(app.getHttpServer())
          .get('/rewards/summary')
          .expect(401);
      });

      it('should return reward summary with valid token', () => {
        const token = createTestToken();
        return request(app.getHttpServer())
          .get('/rewards/summary')
          .set('Authorization', `Bearer ${token}`)
          .expect(200)
          .expect((res) => {
            expect(res.body.pendingUsdt).toBe(1000);
            expect(res.body.settleableUsdt).toBe(500);
            expect(res.body.settledTotalUsdt).toBe(2000);
            expect(res.body.expiredTotalUsdt).toBe(100);
          });
      });
    });

    describe('GET /rewards/details', () => {
      it('should return 401 without auth token', () => {
        return request(app.getHttpServer())
          .get('/rewards/details')
          .expect(401);
      });

      it('should return paginated reward details with valid token', () => {
        const token = createTestToken();
        return request(app.getHttpServer())
          .get('/rewards/details')
          .set('Authorization', `Bearer ${token}`)
          .expect(200)
          .expect((res) => {
            expect(res.body.data).toHaveLength(1);
            expect(res.body.data[0].usdtAmount).toBe(500);
            expect(res.body.pagination.total).toBe(1);
          });
      });

      it('should accept filter parameters', () => {
        const token = createTestToken();
        return request(app.getHttpServer())
          .get('/rewards/details')
          .query({ status: RewardStatus.PENDING, page: 1, pageSize: 10 })
          .set('Authorization', `Bearer ${token}`)
          .expect(200);
      });
    });

    describe('GET /rewards/pending', () => {
      it('should return 401 without auth token', () => {
        return request(app.getHttpServer())
          .get('/rewards/pending')
          .expect(401);
      });

      it('should return pending rewards with countdown', () => {
        const token = createTestToken();
        return request(app.getHttpServer())
          .get('/rewards/pending')
          .set('Authorization', `Bearer ${token}`)
          .expect(200)
          .expect((res) => {
            expect(res.body).toHaveLength(1);
            expect(res.body[0].usdtAmount).toBe(500);
            expect(res.body[0].remainingTimeMs).toBeGreaterThan(0);
          });
      });
    });
  });

  describe('Settlement API', () => {
    describe('POST /rewards/settle', () => {
      it('should return 401 without auth token', () => {
        return request(app.getHttpServer())
          .post('/rewards/settle')
          .send({ settleCurrency: 'BNB' })
          .expect(401);
      });

      it('should settle rewards successfully with valid token', () => {
        const token = createTestToken();
        return request(app.getHttpServer())
          .post('/rewards/settle')
          .set('Authorization', `Bearer ${token}`)
          .send({ settleCurrency: 'BNB' })
          .expect(201)
          .expect((res) => {
            expect(res.body.success).toBe(true);
            expect(res.body.settledUsdtAmount).toBe(500);
            expect(res.body.receivedAmount).toBe(0.25);
            expect(res.body.settleCurrency).toBe('BNB');
            expect(res.body.txHash).toBe('0x123abc');
          });
      });

      it('should validate settleCurrency parameter', () => {
        const token = createTestToken();
        return request(app.getHttpServer())
          .post('/rewards/settle')
          .set('Authorization', `Bearer ${token}`)
          .send({ settleCurrency: '' })
          .expect(400);
      });

      it('should accept different settlement currencies', async () => {
        const token = createTestToken();
        const currencies = ['BNB', 'OG', 'USDT', 'DST'];

        for (const currency of currencies) {
          mockRewardService.settleRewards.mockResolvedValueOnce({
            success: true,
            settledUsdtAmount: 500,
            receivedAmount: currency === 'USDT' ? 500 : 0.25,
            settleCurrency: currency,
            txHash: '0x123',
          });

          await request(app.getHttpServer())
            .post('/rewards/settle')
            .set('Authorization', `Bearer ${token}`)
            .send({ settleCurrency: currency })
            .expect(201);
        }
      });
    });

    describe('Settlement failure scenarios', () => {
      it('should handle no settleable rewards', () => {
        const token = createTestToken();
        mockRewardService.settleRewards.mockResolvedValueOnce({
          success: false,
          settledUsdtAmount: 0,
          receivedAmount: 0,
          settleCurrency: 'BNB',
          error: '没有可结算的收益',
        });

        return request(app.getHttpServer())
          .post('/rewards/settle')
          .set('Authorization', `Bearer ${token}`)
          .send({ settleCurrency: 'BNB' })
          .expect(201)
          .expect((res) => {
            expect(res.body.success).toBe(false);
            expect(res.body.error).toBe('没有可结算的收益');
          });
      });

      it('should handle wallet service failure', () => {
        const token = createTestToken();
        mockRewardService.settleRewards.mockResolvedValueOnce({
          success: false,
          settledUsdtAmount: 500,
          receivedAmount: 0,
          settleCurrency: 'BNB',
          error: 'Insufficient liquidity',
        });

        return request(app.getHttpServer())
          .post('/rewards/settle')
          .set('Authorization', `Bearer ${token}`)
          .send({ settleCurrency: 'BNB' })
          .expect(201)
          .expect((res) => {
            expect(res.body.success).toBe(false);
            expect(res.body.error).toBe('Insufficient liquidity');
          });
      });
    });
  });
});
