import { Test, TestingModule } from '@nestjs/testing';
import { HttpException, HttpStatus } from '@nestjs/common';
import { SandboxService } from '../sandbox.service';
import { PrismaService } from '../../../prisma/prisma.service';
import { AIGatewayService } from '../../ai/ai-gateway.service';

describe('SandboxService', () => {
  let service: SandboxService;
  let prisma: PrismaService;
  let aiGateway: AIGatewayService;

  const mockPrisma = {
    user: {
      findUnique: jest.fn(),
    },
    sandboxSession: {
      findMany: jest.fn(),
      findUnique: jest.fn(),
      count: jest.fn(),
      create: jest.fn(),
      upsert: jest.fn(),
    },
  };

  const mockAIGateway = {
    chat: jest.fn(),
  };

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

    service = module.get<SandboxService>(SandboxService);
    prisma = module.get<PrismaService>(PrismaService);
    aiGateway = module.get<AIGatewayService>(AIGatewayService);
    jest.clearAllMocks();
  });

  describe('chat', () => {
    it('should throw if user not found', async () => {
      mockPrisma.user.findUnique.mockResolvedValue(null);

      await expect(
        service.chat(1, 'test-conv', 'gpt-3.5', [{ role: 'user', content: 'Hello' }])
      ).rejects.toThrow(HttpException);
    });

    it('should throw if user not active', async () => {
      mockPrisma.user.findUnique.mockResolvedValue({ id: 1, status: 'INACTIVE' });

      await expect(
        service.chat(1, 'test-conv', 'gpt-3.5', [{ role: 'user', content: 'Hello' }])
      ).rejects.toThrow(HttpException);
    });

    it('should throw if daily quota exceeded for new conversation', async () => {
      mockPrisma.user.findUnique.mockResolvedValue({ 
        id: 1, status: 'ACTIVE', sandboxDaily: 10 
      });
      mockPrisma.sandboxSession.findUnique.mockResolvedValue(null);
      mockPrisma.sandboxSession.count.mockResolvedValue(10);

      await expect(
        service.chat(1, 'new-conv', 'gpt-3.5', [{ role: 'user', content: 'Hello' }])
      ).rejects.toThrow(HttpException);
    });

    it('should not count quota for existing conversation', async () => {
      mockPrisma.user.findUnique.mockResolvedValue({ 
        id: 1, status: 'ACTIVE', sandboxDaily: 10 
      });
      mockPrisma.sandboxSession.findUnique.mockResolvedValue({ id: 1 });
      mockAIGateway.chat.mockResolvedValue('AI回复');
      mockPrisma.sandboxSession.upsert.mockResolvedValue({ id: 2 });

      const result = await service.chat(1, 'existing-conv', 'gpt-3.5', [
        { role: 'user', content: 'Hello' }
      ]);

      expect(result.reply).toBe('AI回复');
      expect(mockPrisma.sandboxSession.count).not.toHaveBeenCalled();
    });

    it('should call AI gateway and save session', async () => {
      mockPrisma.user.findUnique.mockResolvedValue({ 
        id: 1, status: 'ACTIVE', sandboxDaily: 10 
      });
      mockPrisma.sandboxSession.findUnique.mockResolvedValue(null);
      mockPrisma.sandboxSession.count.mockResolvedValue(5);
      mockAIGateway.chat.mockResolvedValue('AI回复');
      mockPrisma.sandboxSession.upsert.mockResolvedValue({ id: 1 });

      const result = await service.chat(1, 'test-conv', 'gpt-3.5', [
        { role: 'user', content: 'Hello' }
      ]);

      expect(result.reply).toBe('AI回复');
      expect(result.conversationId).toBe('test-conv');
      expect(result.sessionId).toBeDefined();
      expect(mockAIGateway.chat).toHaveBeenCalledWith('gpt-3.5', expect.any(Array), undefined);
      expect(mockPrisma.sandboxSession.upsert).toHaveBeenCalled();
    });
  });

  describe('getHistory', () => {
    it('should return paginated history', async () => {
      const mockSessions = [
        { id: 1, conversationId: 'conv-1', model: 'gpt-3.5', title: 'Test', createdAt: new Date(), tokens: 10 }
      ];
      mockPrisma.sandboxSession.findMany.mockResolvedValue(mockSessions);
      mockPrisma.sandboxSession.count.mockResolvedValue(1);

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

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

  describe('getQuota', () => {
    it('should return quota info', async () => {
      mockPrisma.user.findUnique.mockResolvedValue({ 
        id: 1, sandboxDaily: 10, sandboxExtra: 0 
      });
      mockPrisma.sandboxSession.count.mockResolvedValue(3);

      const result = await service.getQuota(1);

      expect(result).toEqual({
        dailyLimit: 10,
        used: 3,
        dailyRemaining: 7,
        extra: 0,
        canUse: true,
        totalRemaining: 7,
      });
    });
  });
});
