/**
 * AI Vision 处理器（plan.md「3.5 Layer 3: AI Vision」+「1.1 ocr 目录」）。
 *
 * Layer 3 可选高精度识别：把图片发送到云端 AI（GLM-4V / GPT-4V 等），
 * 返回结构化账单信息。
 *
 * 从 ocrProcessor.ts 抽出为独立模块（plan.md 1.1 目录树点名 src/ocr/AiVisionProcessor.ts），
 * 实现 AiVisionProvider 接口。
 */

import type { AiVisionProvider } from '../../domain/pipeline/ocrProcessor';
import type { ImportedEvent } from '../../domain/core/types';
import { parseAiJson, sanitizeForAi } from '../../domain/ai';
import { logger } from '../../utils/logger';

export interface AiVisionConfig {
  enabled: boolean;
  apiUrl: string;
  apiKey: string;
  model: string;     // 如 'glm-4v-flash'
}

/**
 * AI Vision 处理器：调用云端多模态大模型识别账单图片。
 *
 * 生产环境注入 fetch，测试环境注入 mock。
 */
export class AiVisionProcessor implements AiVisionProvider {
  constructor(
    private readonly config: AiVisionConfig,
    private readonly fetchImpl: (url: string, init: RequestInit) => Promise<Response> = fetch,
  ) {}

  /**
   * 识别图片中的账单。
   * @param imageBase64 base64 图片（可含 data:image/... 前缀）
   * @returns 账单事件或 null
   */
  async recognizeBill(imageBase64: string): Promise<ImportedEvent | null> {
    if (!this.config.enabled || !this.config.apiKey) return null;

    // 安全校验：API URL 必须是 HTTPS，防止 bearer token 明文传输
    if (!this.config.apiUrl.startsWith('https://')) {
      throw new Error('AI API URL must use HTTPS');
    }

    const prompt = `识别这张账单/支付截图中的信息，返回 JSON：
{
  "type": "expense" | "income" | "transfer",
  "amount": "数字",
  "currency": "CNY",
  "counterparty": "商户/对方",
  "narration": "备注(≤15字)",
  "time": "ISO8601 或可解析的日期时间"
}
非账单图片返回空 JSON {}。只返回 JSON。`;

    const startTime = Date.now();
    try {
      const response = await this.fetchImpl(`${this.config.apiUrl}/chat/completions`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.config.apiKey}`,
        },
        body: JSON.stringify({
          model: this.config.model,
          messages: [{
            role: 'user',
            content: [
              { type: 'text', text: prompt },
              { type: 'image_url', image_url: { url: imageBase64 } },
            ],
          }],
          temperature: 0.1,
        }),
      });

      const duration = Date.now() - startTime;

      if (!response.ok) {
        const errorText = await response.text().catch(() => '无法读取 Error Body');
        logger.error('AiVision', `AI Vision API 请求失败 [HTTP ${response.status}] (耗时: ${duration}ms, 模型: ${this.config.model}): ${errorText}`);
        return null;
      }
      const data = await response.json() as { choices: { message: { content: string } }[] };
      const content = data.choices[0]?.message?.content ?? '';
      const parsed = parseAiJson<{
        type: 'expense' | 'income' | 'transfer';
        amount: string;
        currency: string;
        counterparty: string;
        narration: string;
        time: string;
      }>(content);

      if (!parsed || !parsed.amount) {
        logger.debug('AiVision', `AI Vision 响应解析为空或缺少金额 (耗时: ${duration}ms, 模型: ${this.config.model})`);
        return null;
      }

      const amount = parseFloat(parsed.amount);
      logger.debug('AiVision', `AI Vision 识别成功 (耗时: ${duration}ms, 模型: ${this.config.model}): 金额 ${amount} ${parsed.currency || 'CNY'}, 对方: ${parsed.counterparty || '未知'}`);
      return {
        id: `ai-vision:${Date.now()}:${amount}`,
        occurredAt: parsed.time || new Date().toISOString().slice(0, 16),
        amount: parsed.type === 'expense' ? `-${amount}` : String(amount),
        currency: parsed.currency || 'CNY',
        direction: parsed.type === 'income' ? 'income' : parsed.type === 'transfer' ? 'transfer' : 'expense',
        counterparty: sanitizeForAi(parsed.counterparty || ''),
        memo: 'AI Vision 识别',
        raw: { source: 'ai-vision', model: this.config.model },
      };
    } catch (e) {
      logger.error('AiVision', `AI Vision 异常 (模型: ${this.config.model})`, e);
      return null;
    }
  }
}

/** 模拟 AI Vision 处理器（测试用）。 */
export class MockAiVisionProcessor implements AiVisionProvider {
  constructor(private response: ImportedEvent | null = null) {}
  async recognizeBill(_imageBase64: string): Promise<ImportedEvent | null> {
    return this.response;
  }
  /** 测试辅助。 */
  setResponse(event: ImportedEvent | null): void {
    this.response = event;
  }
}
