import { createHash } from 'crypto';
import Anthropic from '@anthropic-ai/sdk';
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import type { TransferCategory, TransferCondition } from '@prisma/client';
import { CachePrefix, CacheService } from '@modules/shared';

export interface VisionAssessmentInput {
  imageUrls?: string[];
  imageBase64?: { data: string; mediaType: 'image/jpeg' | 'image/png' | 'image/webp' | 'image/gif' }[];
}

export interface VisionAssessmentResult {
  condition: TransferCondition;
  conditionConfidence: number;
  detectedBrand: string | null;
  detectedCategory: TransferCategory | null;
  qualityScore: number; // 0-100
  description: string;
}

export const CLAUDE_VISION_SERVICE = Symbol('CLAUDE_VISION_SERVICE');

export interface IClaudeVisionService {
  assessFurnitureCondition(input: VisionAssessmentInput): Promise<VisionAssessmentResult>;
  isAvailable(): boolean;
}

@Injectable()
export class ClaudeVisionService implements IClaudeVisionService {
  private readonly logger = new Logger(ClaudeVisionService.name);
  private readonly client: Anthropic | null;
  private readonly model = 'claude-sonnet-4-20250514';

  constructor(
    private readonly config: ConfigService,
    private readonly cache: CacheService,
  ) {
    const apiKey = this.config.get<string>('CLAUDE_API_KEY');
    this.client = apiKey ? new Anthropic({ apiKey }) : null;

    if (!this.client) {
      this.logger.warn('CLAUDE_API_KEY not configured — Vision assessment will use fallback.');
    }
  }

  isAvailable(): boolean {
    return this.client !== null;
  }

  async assessFurnitureCondition(input: VisionAssessmentInput): Promise<VisionAssessmentResult> {
    const cacheKey = this.buildCacheKey(input);
    const cached = await this.tryGetCached(cacheKey);
    if (cached) return cached;

    if (!this.client) {
      return this.fallbackAssessment();
    }

    try {
      const imageContent = this.buildImageContent(input);
      if (imageContent.length === 0) {
        return this.fallbackAssessment();
      }

      const response = await this.client.messages.create({
        model: this.model,
        max_tokens: 1024,
        system: SYSTEM_PROMPT,
        messages: [
          {
            role: 'user',
            content: [
              ...imageContent,
              { type: 'text', text: USER_PROMPT },
            ],
          },
        ],
      });

      const text = response.content
        .filter((block): block is Anthropic.TextBlock => block.type === 'text')
        .map((block) => block.text)
        .join('');

      const result = this.parseAssessment(text);
      await this.cacheResult(cacheKey, result);
      return result;
    } catch (error) {
      this.logger.error(
        `Claude Vision API error: ${error instanceof Error ? error.message : 'Unknown'}`,
        error instanceof Error ? error.stack : undefined,
      );
      return this.fallbackAssessment();
    }
  }

  private buildImageContent(
    input: VisionAssessmentInput,
  ): Anthropic.Messages.ImageBlockParam[] {
    const blocks: Anthropic.Messages.ImageBlockParam[] = [];

    if (input.imageUrls) {
      for (const url of input.imageUrls.slice(0, 4)) {
        blocks.push({
          type: 'image',
          source: { type: 'url', url },
        });
      }
    }

    if (input.imageBase64) {
      for (const img of input.imageBase64.slice(0, 4)) {
        blocks.push({
          type: 'image',
          source: {
            type: 'base64',
            media_type: img.mediaType,
            data: img.data,
          },
        });
      }
    }

    return blocks.slice(0, 4); // Claude max 4 images per request
  }

  private parseAssessment(text: string): VisionAssessmentResult {
    try {
      const jsonMatch = text.match(/\{[\s\S]*\}/);
      if (!jsonMatch) return this.fallbackAssessment();

      const parsed = JSON.parse(jsonMatch[0]) as Record<string, unknown>;

      const condition = this.mapCondition(parsed['condition'] as string);
      const conditionConfidence = Math.max(0, Math.min(1, Number(parsed['conditionConfidence']) || 0.5));
      const detectedBrand = typeof parsed['brand'] === 'string' && parsed['brand'] !== 'unknown'
        ? parsed['brand']
        : null;
      const detectedCategory = this.mapCategory(parsed['category'] as string);
      const qualityScore = Math.max(0, Math.min(100, Math.round(Number(parsed['qualityScore']) || 50)));
      const description = typeof parsed['description'] === 'string'
        ? parsed['description']
        : 'Không có mô tả';

      return { condition, conditionConfidence, detectedBrand, detectedCategory, qualityScore, description };
    } catch {
      this.logger.warn('Failed to parse Vision response, using fallback');
      return this.fallbackAssessment();
    }
  }

  private mapCondition(raw: string | undefined): TransferCondition {
    const map: Record<string, TransferCondition> = {
      new: 'NEW',
      'like-new': 'LIKE_NEW',
      like_new: 'LIKE_NEW',
      good: 'GOOD',
      fair: 'FAIR',
      poor: 'WORN',
      worn: 'WORN',
    };
    return map[raw?.toLowerCase() ?? ''] ?? 'GOOD';
  }

  private mapCategory(raw: string | undefined): TransferCategory | null {
    const map: Record<string, TransferCategory> = {
      furniture: 'FURNITURE',
      appliance: 'APPLIANCE',
      office_equipment: 'OFFICE_EQUIPMENT',
      kitchen: 'KITCHEN',
      premises: 'PREMISES',
      full_unit: 'FULL_UNIT',
    };
    return map[raw?.toLowerCase() ?? ''] ?? null;
  }

  private fallbackAssessment(): VisionAssessmentResult {
    return {
      condition: 'GOOD',
      conditionConfidence: 0.3,
      detectedBrand: null,
      detectedCategory: null,
      qualityScore: 50,
      description: 'Đánh giá AI không khả dụng — sử dụng giá trị mặc định.',
    };
  }

  private buildCacheKey(input: VisionAssessmentInput): string {
    const hash = createHash('sha256');
    if (input.imageUrls) hash.update(input.imageUrls.sort().join('|'));
    if (input.imageBase64) hash.update(input.imageBase64.map((i) => i.data.slice(0, 64)).join('|'));
    return `${CachePrefix.VALUATION}:vision:${hash.digest('hex').slice(0, 16)}`;
  }

  private async tryGetCached(key: string): Promise<VisionAssessmentResult | null> {
    try {
      return await this.cache.getOrSet<VisionAssessmentResult | null>(
        key,
        () => Promise.resolve(null),
        0, // TTL 0 = just try to GET, don't set
        'vision_assessment',
      );
    } catch {
      return null;
    }
  }

  private async cacheResult(key: string, result: VisionAssessmentResult): Promise<void> {
    try {
      await this.cache.getOrSet(
        key,
        () => Promise.resolve(result),
        3600, // Cache for 1 hour
        'vision_assessment',
      );
    } catch {
      // Cache failure is non-critical
    }
  }
}

const SYSTEM_PROMPT = `You are a furniture and home appliance condition assessor for a Vietnamese real estate transfer marketplace.
Analyze the provided images and assess the item's condition for pricing purposes.
Respond ONLY with a JSON object — no markdown, no explanation outside the JSON.`;

const USER_PROMPT = `Assess the furniture/appliance in these images. Return a JSON object with EXACTLY these fields:
{
  "condition": "new" | "like-new" | "good" | "fair" | "poor",
  "conditionConfidence": 0.0-1.0,
  "brand": "detected brand name or unknown",
  "category": "furniture" | "appliance" | "office_equipment" | "kitchen" | "premises" | "full_unit",
  "qualityScore": 0-100,
  "description": "Brief Vietnamese description of the item's condition and notable features"
}`;
