export type ExtractMaterialsErrorCode =
  | "LLM_RATE_LIMIT"
  | "LLM_TIMEOUT"
  | "LLM_INVALID_RESPONSE"
  | "LLM_PROVIDER_UNSUPPORTED_INPUT"
  | "DOCUMENT_CONVERSION_FAILED"
  | "DB_WRITE_FAILED"
  | "DOCUMENT_EXTRACTION_FAILED";

export interface ExtractMaterialsErrorContext {
  batchId: string;
  sourceFileId: string;
  sourceFileName: string;
  extractionModel?: string | null;
}

export interface ExtractMaterialsErrorBody {
  success: false;
  code: ExtractMaterialsErrorCode;
  retryable: boolean;
  error: string;
  raw_error: string;
  batch_id: string;
  source_file_id: string;
  source_file_name: string;
}

export interface ExtractMaterialsErrorResponse {
  status: number;
  body: ExtractMaterialsErrorBody;
}

function getErrorMessage(error: unknown): string {
  return error instanceof Error ? error.message : String(error);
}

function getErrorStatus(error: unknown): number | null {
  if (!error || typeof error !== "object") {
    return null;
  }

  const candidate = error as {
    status?: unknown;
    statusCode?: unknown;
    response?: { status?: unknown; status_code?: unknown };
  };

  if (typeof candidate.status === "number") return candidate.status;
  if (typeof candidate.statusCode === "number") return candidate.statusCode;
  if (typeof candidate.response?.status === "number") return candidate.response.status;
  if (typeof candidate.response?.status_code === "number") return candidate.response.status_code;
  return null;
}

function includesAny(message: string, needles: string[]): boolean {
  return needles.some((needle) => message.includes(needle));
}

function classifyExtractMaterialsError(error: unknown): {
  code: ExtractMaterialsErrorCode;
  status: number;
  retryable: boolean;
} {
  const status = getErrorStatus(error);
  const message = getErrorMessage(error).toLowerCase();

  if (
    status === 429
    || includesAny(message, ["429", "rate limit", "rate_limit", "速率限制", "请求频率"])
  ) {
    return { code: "LLM_RATE_LIMIT", status: 429, retryable: true };
  }

  if (includesAny(message, ["timeout", "timed out", "超时"])) {
    return { code: "LLM_TIMEOUT", status: 500, retryable: true };
  }

  if (includesAny(message, ["不是合法 json", "invalid json", "固定 json 合同", "invalid response"])) {
    return { code: "LLM_INVALID_RESPONSE", status: 500, retryable: false };
  }

  if (includesAny(message, ["不支持图片输入", "不支持原始文件输入", "unsupported image input", "unsupported file input"])) {
    return { code: "LLM_PROVIDER_UNSUPPORTED_INPUT", status: 500, retryable: false };
  }

  if (includesAny(message, ["文档转换", "document conversion", "conversion failed"])) {
    return { code: "DOCUMENT_CONVERSION_FAILED", status: 500, retryable: false };
  }

  if (includesAny(message, ["procurement_materials", "db write", "database", "写入文档识别物料失败"])) {
    return { code: "DB_WRITE_FAILED", status: 500, retryable: false };
  }

  return { code: "DOCUMENT_EXTRACTION_FAILED", status: 500, retryable: false };
}

function userMessageFor(code: ExtractMaterialsErrorCode, context: ExtractMaterialsErrorContext): string {
  if (code === "LLM_RATE_LIMIT") {
    const model = context.extractionModel?.trim();
    return model ? `${model} 调用达到速率限制，请稍后重试` : "模型调用达到速率限制，请稍后重试";
  }
  if (code === "LLM_TIMEOUT") {
    return "模型请求超时，请稍后重试";
  }
  if (code === "LLM_INVALID_RESPONSE") {
    return "模型返回内容不符合固定 JSON 合同，请检查文件内容或稍后重试";
  }
  if (code === "LLM_PROVIDER_UNSUPPORTED_INPUT") {
    return "文档识别模型配置不支持图片/附件输入，请联系管理员调整 OCR Provider";
  }
  if (code === "DOCUMENT_CONVERSION_FAILED") {
    return "文档转换失败，请检查文件格式后重试";
  }
  if (code === "DB_WRITE_FAILED") {
    return "识别结果写入数据库失败，请联系管理员检查数据库状态";
  }
  return "文档识别失败，请检查后端日志";
}

export function buildExtractMaterialsErrorResponse(
  error: unknown,
  context: ExtractMaterialsErrorContext,
): ExtractMaterialsErrorResponse {
  const rawError = getErrorMessage(error);
  const classification = classifyExtractMaterialsError(error);

  return {
    status: classification.status,
    body: {
      success: false,
      code: classification.code,
      retryable: classification.retryable,
      error: userMessageFor(classification.code, context),
      raw_error: rawError,
      batch_id: context.batchId,
      source_file_id: context.sourceFileId,
      source_file_name: context.sourceFileName,
    },
  };
}
