/**
 * ============================================================
 * documentExtractionService.ts — 文档提取前端 service
 * ============================================================
 * 调用后端 /api/extract-materials 端点，识别非 Excel 文件并返回已落库物料。
 * ============================================================
 */

import type { ProcurementMaterial } from '@/types';

const DEFAULT_AUTOMATION_PORT = '3030';

function resolveAutomationBaseUrl(): string {
  const explicit = (import.meta.env.VITE_AUTOMATION_BASE_URL as string | undefined)?.trim();
  if (explicit) return explicit.replace(/\/+$/, '');
  if (import.meta.env.DEV) return '';

  const port = (import.meta.env.VITE_AUTOMATION_PORT as string | undefined) ?? DEFAULT_AUTOMATION_PORT;
  if (typeof window !== 'undefined') {
    return `${window.location.protocol}//${window.location.hostname}${port ? `:${port}` : ''}`;
  }
  return `http://127.0.0.1:${port}`;
}

const BASE_URL = resolveAutomationBaseUrl();

export interface DocumentExtractionResult {
  materials: ProcurementMaterial[];
  summary: string;
  warnings: string[];
  elapsed: string;
  extractionModel: string;
}

export interface RecognizeDocumentMaterialsOptions {
  batchId: string;
  sourceFileId: string;
  sourceFileName: string;
  createdBy?: string;
}

export interface DocumentExtractionErrorInit {
  status: number;
  code?: string;
  retryable?: boolean;
  batchId?: string;
  sourceFileName?: string;
  rawError?: string;
}

export class DocumentExtractionError extends Error {
  readonly status: number;
  readonly code?: string;
  readonly retryable: boolean;
  readonly batchId?: string;
  readonly sourceFileName?: string;
  readonly rawError?: string;

  constructor(message: string, init: DocumentExtractionErrorInit) {
    super(message);
    this.name = 'DocumentExtractionError';
    this.status = init.status;
    this.code = init.code;
    this.retryable = Boolean(init.retryable);
    this.batchId = init.batchId;
    this.sourceFileName = init.sourceFileName;
    this.rawError = init.rawError;
  }
}

function normalizeDocumentExtractionErrorMessage(status: number, code: string | undefined, fallback: string): string {
  if (status === 429 || code === 'LLM_RATE_LIMIT') {
    return '模型调用达到速率限制，文件已保留为附件，请稍后重试识别。';
  }
  return fallback || `服务端错误 (${status})`;
}

/**
 * 上传非 Excel 原始文件到后端文档提取 LLM 识别接口。
 * 后端负责校验模型 JSON、写入 procurement_materials，并返回已落库记录。
 */
export async function recognizeMaterialsFromDocument(
  file: File,
  options: RecognizeDocumentMaterialsOptions,
): Promise<DocumentExtractionResult> {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('batch_id', options.batchId);
  formData.append('source_file_id', options.sourceFileId);
  formData.append('source_file_name', options.sourceFileName);
  if (options.createdBy) {
    formData.append('created_by', options.createdBy);
  }

  const res = await fetch(`${BASE_URL}/api/extract-materials`, {
    method: 'POST',
    body: formData,
  });

  if (!res.ok) {
    let errorBody: {
      error?: string;
      code?: string;
      retryable?: boolean;
      raw_error?: string;
      batch_id?: string;
      source_file_name?: string;
    } | null = null;
    try {
      errorBody = await res.json() as typeof errorBody;
    } catch {
      errorBody = null;
    }
    const fallback = errorBody?.error || `服务端错误 (${res.status}): ${res.statusText}`;
    throw new DocumentExtractionError(
      normalizeDocumentExtractionErrorMessage(res.status, errorBody?.code, fallback),
      {
        status: res.status,
        code: errorBody?.code,
        retryable: errorBody?.retryable,
        batchId: errorBody?.batch_id,
        sourceFileName: errorBody?.source_file_name,
        rawError: errorBody?.raw_error,
      },
    );
  }

  const body = await res.json() as {
    success: boolean;
    materials?: ProcurementMaterial[];
    items?: ProcurementMaterial[];
    summary: string;
    warnings?: string[];
    elapsed: string;
    extraction_model?: string;
    error?: string;
    code?: string;
    retryable?: boolean;
    raw_error?: string;
    batch_id?: string;
    source_file_name?: string;
  };

  if (!body.success) {
    const fallback = body.error || '未知错误';
    throw new DocumentExtractionError(
      normalizeDocumentExtractionErrorMessage(200, body.code, fallback),
      {
        status: 200,
        code: body.code,
        retryable: body.retryable,
        batchId: body.batch_id,
        sourceFileName: body.source_file_name,
        rawError: body.raw_error,
      },
    );
  }

  return {
    materials: body.materials ?? body.items ?? [],
    summary: body.summary,
    warnings: body.warnings ?? [],
    elapsed: body.elapsed,
    extractionModel: body.extraction_model ?? '',
  };
}
