import type { IClaudeVisionService, VisionAssessmentResult } from '../../infrastructure/services/claude-vision.service';
import { EstimateFromPhotosCommand } from '../commands/estimate-from-photos/estimate-from-photos.command';
import { EstimateFromPhotosHandler } from '../commands/estimate-from-photos/estimate-from-photos.handler';

function mockVisionResult(overrides?: Partial<VisionAssessmentResult>): VisionAssessmentResult {
  return {
    condition: 'GOOD',
    conditionConfidence: 0.85,
    detectedBrand: null,
    detectedCategory: null,
    qualityScore: 70,
    description: 'Ghế sofa tình trạng tốt, ít trầy xước.',
    ...overrides,
  };
}

describe('EstimateFromPhotosHandler', () => {
  let handler: EstimateFromPhotosHandler;
  let mockVisionService: { [K in keyof IClaudeVisionService]: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockVisionService = {
      assessFurnitureCondition: vi.fn(),
      isAvailable: vi.fn().mockReturnValue(true),
    };

    handler = new EstimateFromPhotosHandler(
      mockVisionService as any,
      { error: vi.fn() } as any,
    );
  });

  it('returns vision assessment with pricing estimates', async () => {
    mockVisionService.assessFurnitureCondition.mockResolvedValue(
      mockVisionResult({ condition: 'LIKE_NEW', detectedBrand: 'IKEA' }),
    );

    const command = new EstimateFromPhotosCommand([
      {
        imageUrls: ['https://example.com/sofa.jpg'],
        category: 'FURNITURE',
        originalPriceVND: 15_000_000,
        purchaseYear: 2024,
      },
    ]);

    const result = await handler.execute(command);

    expect(result.visionAvailable).toBe(true);
    expect(result.items).toHaveLength(1);
    expect(result.items[0].visionAssessment.condition).toBe('LIKE_NEW');
    expect(result.items[0].visionAssessment.detectedBrand).toBe('IKEA');
    expect(result.items[0].visionAdjustedEstimate).not.toBeNull();
    expect(result.items[0].ruleBasedEstimate).not.toBeNull();
  });

  it('returns null pricing when category/price/year not provided', async () => {
    mockVisionService.assessFurnitureCondition.mockResolvedValue(mockVisionResult());

    const command = new EstimateFromPhotosCommand([
      { imageUrls: ['https://example.com/chair.jpg'] },
    ]);

    const result = await handler.execute(command);

    expect(result.items[0].visionAssessment).toBeDefined();
    expect(result.items[0].ruleBasedEstimate).toBeNull();
    expect(result.items[0].visionAdjustedEstimate).toBeNull();
  });

  it('uses Vision-detected category when user did not provide one', async () => {
    mockVisionService.assessFurnitureCondition.mockResolvedValue(
      mockVisionResult({ detectedCategory: 'APPLIANCE', condition: 'FAIR' }),
    );

    const command = new EstimateFromPhotosCommand([
      {
        imageUrls: ['https://example.com/washer.jpg'],
        originalPriceVND: 10_000_000,
        purchaseYear: 2022,
      },
    ]);

    const result = await handler.execute(command);

    // visionAdjustedEstimate should use APPLIANCE category
    expect(result.items[0].visionAdjustedEstimate).not.toBeNull();
    // ruleBasedEstimate should be null since no user-provided category
    expect(result.items[0].ruleBasedEstimate).toBeNull();
  });

  it('handles multiple items in batch', async () => {
    mockVisionService.assessFurnitureCondition
      .mockResolvedValueOnce(mockVisionResult({ condition: 'LIKE_NEW' }))
      .mockResolvedValueOnce(mockVisionResult({ condition: 'WORN' }));

    const command = new EstimateFromPhotosCommand([
      {
        imageUrls: ['https://example.com/item1.jpg'],
        category: 'FURNITURE',
        originalPriceVND: 10_000_000,
        purchaseYear: 2023,
      },
      {
        imageUrls: ['https://example.com/item2.jpg'],
        category: 'KITCHEN',
        originalPriceVND: 5_000_000,
        purchaseYear: 2020,
      },
    ]);

    const result = await handler.execute(command);

    expect(result.items).toHaveLength(2);
    expect(result.items[0].visionAssessment.condition).toBe('LIKE_NEW');
    expect(result.items[1].visionAssessment.condition).toBe('WORN');
    expect(mockVisionService.assessFurnitureCondition).toHaveBeenCalledTimes(2);
  });

  it('reports visionAvailable=false when service unavailable', async () => {
    mockVisionService.isAvailable.mockReturnValue(false);
    mockVisionService.assessFurnitureCondition.mockResolvedValue(
      mockVisionResult({ conditionConfidence: 0.3 }),
    );

    const command = new EstimateFromPhotosCommand([
      { imageUrls: ['https://example.com/img.jpg'] },
    ]);

    const result = await handler.execute(command);

    expect(result.visionAvailable).toBe(false);
    // Still returns results (fallback)
    expect(result.items).toHaveLength(1);
  });

  it('uses Vision-detected brand for pricing adjustment', async () => {
    mockVisionService.assessFurnitureCondition.mockResolvedValue(
      mockVisionResult({ detectedBrand: 'Herman Miller', condition: 'GOOD' }),
    );

    const command = new EstimateFromPhotosCommand([
      {
        imageUrls: ['https://example.com/chair.jpg'],
        category: 'OFFICE_EQUIPMENT',
        originalPriceVND: 30_000_000,
        purchaseYear: 2023,
      },
    ]);

    const result = await handler.execute(command);

    // Vision-adjusted should use Herman Miller brand multiplier (1.3x)
    const adjusted = result.items[0].visionAdjustedEstimate;
    const ruleBased = result.items[0].ruleBasedEstimate;
    expect(adjusted).not.toBeNull();
    expect(ruleBased).not.toBeNull();
    // Herman Miller (premium) should yield higher estimate than no-brand rule-based
    expect(Number(adjusted!.estimatedPriceVND)).toBeGreaterThan(Number(ruleBased!.estimatedPriceVND));
  });

  it('passes image data correctly to vision service', async () => {
    mockVisionService.assessFurnitureCondition.mockResolvedValue(mockVisionResult());

    const command = new EstimateFromPhotosCommand([
      {
        imageUrls: ['https://example.com/a.jpg', 'https://example.com/b.jpg'],
        imageBase64: [{ data: 'base64data', mediaType: 'image/jpeg' }],
      },
    ]);

    await handler.execute(command);

    expect(mockVisionService.assessFurnitureCondition).toHaveBeenCalledWith({
      imageUrls: ['https://example.com/a.jpg', 'https://example.com/b.jpg'],
      imageBase64: [{ data: 'base64data', mediaType: 'image/jpeg' }],
    });
  });
});
