/**
 * automation/core/llm-client.ts
 *
 * 统一的 OpenAI 兼容客户端工厂。两种构建模式：
 *
 *   - createLlmClient(options)        — DB 优先 + .env 回退；缺配置时抛错。
 *   - createVisionClientFromEnv(opts) — 仅读 .env；缺配置时返回 null（用于
 *                                       视觉降级路径）。
 *
 * 注：由 row-pipeline 通过 `node`（而非 tsx）拉起的 Playwright 子进程
 * (search-plan.js / search-table.js) 暂未迁移到本工厂——这些 .js 文件
 * 无法 import 本 .ts 模块。该部分待 F7 重塑路由层时一并改造。
 */

import OpenAI from "openai";

import { getActiveLlmConfig } from "../internal/db.ts";

export interface ResolvedLlmRuntimeConfig {
  baseURL: string;
  apiKey: string;
  model: string;
  provider: string;
}

export interface LlmClientOptions {
  /** OpenAI SDK maxRetries (默认 0) */
  maxRetries?: number;
  /** 请求超时毫秒数（不传则使用 SDK 默认） */
  timeout?: number;
}

function trimTrailingSlash(value: string): string {
  return value.replace(/\/+$/, "");
}

/**
 * 解析 LLM 运行配置：优先 Supabase llm_config 表，回退到 .env。
 * 两个来源都缺少 baseURL/apiKey 时抛错。
 */
export async function resolveLlmRuntimeConfig(): Promise<ResolvedLlmRuntimeConfig> {
  let baseURL: string | undefined;
  let apiKey: string | undefined;
  let model: string | undefined;
  let provider = "env";

  try {
    const dbConfig = await getActiveLlmConfig();
    if (dbConfig) {
      baseURL = dbConfig.base_url;
      apiKey = dbConfig.api_key;
      model = dbConfig.model;
      provider = dbConfig.provider;
      console.log(`[LLM] 使用数据库配置: provider=${provider}, model=${model}`);
    }
  } catch (err) {
    console.warn("[LLM] 从数据库加载配置失败，回退到环境变量:", err);
  }

  if (!baseURL || !apiKey) {
    baseURL = process.env.LLM_BASE_URL;
    apiKey = process.env.LLM_API_KEY;
    model = process.env.LLM_MODEL ?? "gemini-2.5-pro-preview-p";
    provider = "env";
    console.log(`[LLM] 使用环境变量配置: model=${model}`);
  }

  if (!baseURL || !apiKey) {
    throw new Error(
      "缺少 LLM 配置。请在管理面板配置模型，或在 .env 中填写 LLM_BASE_URL 和 LLM_API_KEY。",
    );
  }

  return {
    baseURL: trimTrailingSlash(baseURL),
    apiKey,
    model: model ?? "gemini-2.5-pro-preview-p",
    provider,
  };
}

function buildClientOptions(
  config: { baseURL: string; apiKey: string },
  options: LlmClientOptions,
): ConstructorParameters<typeof OpenAI>[0] {
  const clientOptions: ConstructorParameters<typeof OpenAI>[0] = {
    baseURL: config.baseURL,
    apiKey: config.apiKey,
    maxRetries: options.maxRetries ?? 0,
  };
  if (options.timeout !== undefined) {
    clientOptions.timeout = options.timeout;
  }
  return clientOptions;
}

/**
 * 构建一个 DB-aware 的 OpenAI 兼容客户端。
 *
 * 调用方负责自己的缓存策略：本工厂每次返回新实例。需要长期复用的
 * 调用点（如 standardization-llm 的 _client 单例）请在外层做记忆化。
 */
export async function createLlmClient(
  options: LlmClientOptions = {},
): Promise<{ client: OpenAI; config: ResolvedLlmRuntimeConfig }> {
  const config = await resolveLlmRuntimeConfig();
  const client = new OpenAI(buildClientOptions(config, options));
  return { client, config };
}

/**
 * 仅从 .env 构建一个视觉/降级用 OpenAI 客户端。
 * LLM_BASE_URL 或 LLM_API_KEY 缺失时返回 null，方便调用方做 graceful degradation。
 */
export function createVisionClientFromEnv(
  options: LlmClientOptions = {},
): OpenAI | null {
  const baseURL = process.env.LLM_BASE_URL;
  const apiKey = process.env.LLM_API_KEY;
  if (!baseURL || !apiKey) {
    return null;
  }
  return new OpenAI(
    buildClientOptions({ baseURL: trimTrailingSlash(baseURL), apiKey }, options),
  );
}
