import { ClaudeVisionService } from '../services/claude-vision.service';

// We test the service with mocked Anthropic client
// The constructor reads CLAUDE_API_KEY from ConfigService

describe('ClaudeVisionService', () => {
  let service: ClaudeVisionService;
  let mockConfig: { get: ReturnType<typeof vi.fn> };
  let mockCache: {
    getOrSet: ReturnType<typeof vi.fn>;
  };

  describe('without API key', () => {
    beforeEach(() => {
      mockConfig = { get: vi.fn().mockReturnValue(undefined) };
      mockCache = { getOrSet: vi.fn().mockImplementation(async (_k, loader) => loader()) };
      service = new ClaudeVisionService(mockConfig as any, mockCache as any);
    });

    it('reports unavailable when no API key', () => {
      expect(service.isAvailable()).toBe(false);
    });

    it('returns fallback assessment when no API key', async () => {
      const result = await service.assessFurnitureCondition({
        imageUrls: ['https://example.com/img.jpg'],
      });

      expect(result.condition).toBe('GOOD');
      expect(result.conditionConfidence).toBe(0.3);
      expect(result.description).toContain('không khả dụng');
    });
  });

  describe('with API key (mocked client)', () => {
    let mockCreate: ReturnType<typeof vi.fn>;

    beforeEach(() => {
      mockConfig = { get: vi.fn().mockReturnValue('test-api-key') };
      mockCache = { getOrSet: vi.fn().mockImplementation(async (_k, loader) => loader()) };
      service = new ClaudeVisionService(mockConfig as any, mockCache as any);

      // Replace the private client with a mock
      mockCreate = vi.fn();
      (service as any).client = { messages: { create: mockCreate } };
    });

    it('reports available when API key present', () => {
      expect(service.isAvailable()).toBe(true);
    });

    it('parses valid Vision response', async () => {
      mockCreate.mockResolvedValue({
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              condition: 'like-new',
              conditionConfidence: 0.9,
              brand: 'IKEA',
              category: 'furniture',
              qualityScore: 85,
              description: 'Ghế sofa IKEA tình trạng rất tốt',
            }),
          },
        ],
      });

      const result = await service.assessFurnitureCondition({
        imageUrls: ['https://example.com/sofa.jpg'],
      });

      expect(result.condition).toBe('LIKE_NEW');
      expect(result.conditionConfidence).toBe(0.9);
      expect(result.detectedBrand).toBe('IKEA');
      expect(result.detectedCategory).toBe('FURNITURE');
      expect(result.qualityScore).toBe(85);
    });

    it('maps "poor" condition to WORN', async () => {
      mockCreate.mockResolvedValue({
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              condition: 'poor',
              conditionConfidence: 0.7,
              brand: 'unknown',
              category: 'appliance',
              qualityScore: 20,
              description: 'Máy giặt cũ, nhiều vết rỉ sét',
            }),
          },
        ],
      });

      const result = await service.assessFurnitureCondition({
        imageUrls: ['https://example.com/washer.jpg'],
      });

      expect(result.condition).toBe('WORN');
      expect(result.detectedBrand).toBeNull(); // 'unknown' maps to null
      expect(result.detectedCategory).toBe('APPLIANCE');
    });

    it('returns fallback on API error', async () => {
      mockCreate.mockRejectedValue(new Error('API timeout'));

      const result = await service.assessFurnitureCondition({
        imageUrls: ['https://example.com/img.jpg'],
      });

      expect(result.condition).toBe('GOOD');
      expect(result.conditionConfidence).toBe(0.3);
    });

    it('returns fallback on invalid JSON response', async () => {
      mockCreate.mockResolvedValue({
        content: [{ type: 'text', text: 'This is not valid JSON at all' }],
      });

      const result = await service.assessFurnitureCondition({
        imageUrls: ['https://example.com/img.jpg'],
      });

      expect(result.condition).toBe('GOOD');
      expect(result.conditionConfidence).toBe(0.3);
    });

    it('returns fallback when no images provided', async () => {
      const result = await service.assessFurnitureCondition({});

      expect(result.condition).toBe('GOOD');
      expect(mockCreate).not.toHaveBeenCalled();
    });

    it('clamps confidence and quality score to valid ranges', async () => {
      mockCreate.mockResolvedValue({
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              condition: 'good',
              conditionConfidence: 1.5, // over 1
              brand: 'unknown',
              category: 'furniture',
              qualityScore: 150, // over 100
              description: 'Test',
            }),
          },
        ],
      });

      const result = await service.assessFurnitureCondition({
        imageUrls: ['https://example.com/img.jpg'],
      });

      expect(result.conditionConfidence).toBe(1);
      expect(result.qualityScore).toBe(100);
    });

    it('sends image content to Claude API correctly', async () => {
      mockCreate.mockResolvedValue({
        content: [{ type: 'text', text: '{"condition":"good","conditionConfidence":0.8,"brand":"unknown","category":"furniture","qualityScore":70,"description":"OK"}' }],
      });

      await service.assessFurnitureCondition({
        imageUrls: ['https://example.com/a.jpg'],
        imageBase64: [{ data: 'abc123', mediaType: 'image/jpeg' }],
      });

      expect(mockCreate).toHaveBeenCalledWith(
        expect.objectContaining({
          model: expect.any(String),
          max_tokens: 1024,
          messages: [
            {
              role: 'user',
              content: expect.arrayContaining([
                expect.objectContaining({ type: 'image', source: expect.objectContaining({ type: 'url' }) }),
                expect.objectContaining({ type: 'image', source: expect.objectContaining({ type: 'base64' }) }),
                expect.objectContaining({ type: 'text' }),
              ]),
            },
          ],
        }),
      );
    });
  });
});
