import process from "node:process";

import { createClient } from "@supabase/supabase-js";
import { mergeSupabaseNodeClientOptions } from "./supabase-node-client.js";

const DEFAULT_PROMPT_ENV = "prod";
const DEFAULT_PROMPT_CACHE_TTL_MS = 300_000;

let promptStoreClient = null;
let promptStoreClientCacheKey = "";

const promptCache = new Map();

function normalizeEnvValue(value) {
  return typeof value === "string" ? value.trim() : "";
}

function parsePositiveInteger(value, fallback) {
  const parsed = Number.parseInt(normalizeEnvValue(value), 10);
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
}

function isPlainObject(value) {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}

export class PromptStoreError extends Error {
  constructor(message, details = {}) {
    super(message);
    this.name = "PromptStoreError";
    this.details = details;
  }
}

function toErrorMessage(error) {
  if (error instanceof Error) {
    return error.message;
  }

  if (error && typeof error === "object") {
    const candidate = error;
    if (typeof candidate.message === "string" && candidate.message.trim()) {
      return candidate.message.trim();
    }

    if (typeof candidate.error_description === "string" && candidate.error_description.trim()) {
      return candidate.error_description.trim();
    }
  }

  return String(error);
}

function buildPromptCacheKey(promptKey, targetEnv, provider, model) {
  return `${promptKey}::${targetEnv}::${provider}::${model}`;
}

function getCachedPrompt(cacheKey) {
  const cached = promptCache.get(cacheKey);
  if (!cached) {
    return null;
  }

  if (cached.expiresAt <= Date.now()) {
    promptCache.delete(cacheKey);
    return null;
  }

  return cached.value;
}

function setCachedPrompt(cacheKey, value, ttlMs) {
  promptCache.set(cacheKey, {
    value,
    expiresAt: Date.now() + ttlMs,
  });
}

function normalizePromptRow(promptKey, row) {
  if (!isPlainObject(row)) {
    return null;
  }

  const contentFormat = normalizeEnvValue(row.content_format);
  const payload = isPlainObject(row.payload_json) ? row.payload_json : null;
  if (!contentFormat || !payload) {
    return null;
  }

  return {
    promptKey,
    contentFormat,
    payload,
    variables: Array.isArray(row.variables_json) ? row.variables_json : [],
    version: Number.isInteger(row.version) ? row.version : Number(row.version ?? 0) || 0,
    checksum: normalizeEnvValue(row.checksum),
    targetEnv: normalizeEnvValue(row.target_env) || DEFAULT_PROMPT_ENV,
    providerScope: normalizeEnvValue(row.provider_scope) || "*",
    modelScope: normalizeEnvValue(row.model_scope) || "*",
    templateId: normalizeEnvValue(row.template_id),
    versionId: normalizeEnvValue(row.version_id),
    description: normalizeEnvValue(row.description),
  };
}

export function resolvePromptStoreUrl(env = process.env) {
  return normalizeEnvValue(env.PROMPT_SUPABASE_URL);
}

export function resolvePromptStoreKey(env = process.env) {
  return normalizeEnvValue(env.PROMPT_SUPABASE_SERVICE_ROLE_KEY)
    || normalizeEnvValue(env.PROMPT_SUPABASE_service_key);
}

export function resolvePromptStoreRuntimeConfig(env = process.env) {
  const url = resolvePromptStoreUrl(env);
  const key = resolvePromptStoreKey(env);

  return {
    url,
    key,
    targetEnv: normalizeEnvValue(env.PROMPT_ENV) || DEFAULT_PROMPT_ENV,
    cacheTtlMs: parsePositiveInteger(env.PROMPT_CACHE_TTL_MS, DEFAULT_PROMPT_CACHE_TTL_MS),
    enabled: Boolean(url && key),
  };
}

function getPromptStoreClient(config) {
  if (!config.enabled) {
    return null;
  }

  const nextCacheKey = `${config.url}::${config.key}`;
  if (!promptStoreClient || promptStoreClientCacheKey !== nextCacheKey) {
    promptStoreClient = createClient(
      config.url,
      config.key,
      mergeSupabaseNodeClientOptions({
        auth: {
          autoRefreshToken: false,
          persistSession: false,
        },
      }, process.env),
    );
    promptStoreClientCacheKey = nextCacheKey;
  }

  return promptStoreClient;
}

export function renderPromptTemplate(template, variables = {}) {
  return String(template ?? "").replace(/{{\s*([a-zA-Z0-9_]+)\s*}}/g, (_match, variableName) => {
    const rawValue = variables[variableName];
    if (rawValue === undefined || rawValue === null) {
      return "";
    }

    if (typeof rawValue === "string") {
      return rawValue;
    }

    if (typeof rawValue === "number" || typeof rawValue === "boolean") {
      return String(rawValue);
    }

    if (Array.isArray(rawValue)) {
      return rawValue.map((item) => {
        if (item === undefined || item === null) {
          return "";
        }
        return typeof item === "string" ? item : JSON.stringify(item);
      }).join("");
    }

    if (typeof rawValue === "object") {
      return JSON.stringify(rawValue);
    }

    return String(rawValue);
  });
}

export function renderPromptPayloadStrings(payload, variables = {}) {
  if (Array.isArray(payload)) {
    return payload.map((item) => renderPromptPayloadStrings(item, variables));
  }

  if (isPlainObject(payload)) {
    return Object.fromEntries(
      Object.entries(payload).map(([key, value]) => [key, renderPromptPayloadStrings(value, variables)]),
    );
  }

  if (typeof payload === "string") {
    return renderPromptTemplate(payload, variables);
  }

  return payload;
}

function normalizeExpectedFormats(value) {
  if (!value) {
    return [];
  }

  if (Array.isArray(value)) {
    return value.map((item) => normalizeEnvValue(item)).filter(Boolean);
  }

  const single = normalizeEnvValue(value);
  return single ? [single] : [];
}

function assertPayloadObject(payload, promptKey) {
  if (!isPlainObject(payload)) {
    throw new PromptStoreError(`[prompt-store] ${promptKey} 返回的 payload_json 不是 object`, {
      promptKey,
      reason: "invalid_payload",
    });
  }

  return payload;
}

function assertExpectedContentFormat(prompt, expectedFormats) {
  if (expectedFormats.length === 0 || expectedFormats.includes(prompt.contentFormat)) {
    return;
  }

  throw new PromptStoreError(
    `[prompt-store] ${prompt.promptKey} content_format=${prompt.contentFormat} 与预期 ${expectedFormats.join(", ")} 不一致`,
    {
      promptKey: prompt.promptKey,
      reason: "unexpected_content_format",
      expectedFormats,
      actualContentFormat: prompt.contentFormat,
    },
  );
}

function buildStoreFailure(promptKey, targetEnv, provider, model, reason, error) {
  return new PromptStoreError(
    `[prompt-store] ${promptKey} 远端读取失败: ${reason}`,
    {
      promptKey,
      targetEnv,
      provider,
      model,
      reason,
      error,
    },
  );
}

export function invalidatePromptStoreCache(promptKey) {
  if (!promptKey) {
    promptCache.clear();
    return;
  }

  for (const cacheKey of promptCache.keys()) {
    if (cacheKey.startsWith(`${promptKey}::`)) {
      promptCache.delete(cacheKey);
    }
  }
}

export async function resolvePromptDefinition(promptKey, options = {}) {
  const env = options.env ?? process.env;
  const runtimeConfig = resolvePromptStoreRuntimeConfig(env);
  const targetEnv = normalizeEnvValue(options.targetEnv) || runtimeConfig.targetEnv;
  const provider = normalizeEnvValue(options.provider) || "*";
  const model = normalizeEnvValue(options.model) || "*";
  const expectedFormats = normalizeExpectedFormats(options.expectedContentFormat);
  const cacheKey = buildPromptCacheKey(promptKey, targetEnv, provider, model);

  if (!options.skipCache) {
    const cached = getCachedPrompt(cacheKey);
    if (cached) {
      assertExpectedContentFormat(cached, expectedFormats);
      return cached;
    }
  }

  if (!runtimeConfig.enabled) {
    throw buildStoreFailure(
      promptKey,
      targetEnv,
      provider,
      model,
      "缺少 PROMPT_SUPABASE_URL 或 PROMPT_SUPABASE_SERVICE_ROLE_KEY",
      null,
    );
  }

  const client = getPromptStoreClient(runtimeConfig);
  if (!client) {
    throw buildStoreFailure(promptKey, targetEnv, provider, model, "无法初始化 prompt store 客户端", null);
  }

  try {
    const { data, error } = await client.rpc("prompt_store_resolve_active_version", {
      p_prompt_key: promptKey,
      p_target_env: targetEnv,
      p_provider: provider,
      p_model: model,
    });

    if (error) {
      throw error;
    }

    const firstRow = Array.isArray(data) ? data[0] : data;
    const normalizedRow = normalizePromptRow(promptKey, firstRow);
    if (!normalizedRow) {
      throw buildStoreFailure(
        promptKey,
        targetEnv,
        provider,
        model,
        "未命中 active prompt 版本",
        null,
      );
    }

    const resolved = {
      promptKey,
      source: "store",
      contentFormat: normalizedRow.contentFormat,
      payload: normalizedRow.payload,
      variables: normalizedRow.variables,
      metadata: {
        targetEnv: normalizedRow.targetEnv,
        providerScope: normalizedRow.providerScope,
        modelScope: normalizedRow.modelScope,
        version: normalizedRow.version,
        checksum: normalizedRow.checksum,
        templateId: normalizedRow.templateId,
        versionId: normalizedRow.versionId,
        description: normalizedRow.description,
      },
    };

    assertExpectedContentFormat(resolved, expectedFormats);
    setCachedPrompt(cacheKey, resolved, runtimeConfig.cacheTtlMs);
    return resolved;
  } catch (error) {
    if (error instanceof PromptStoreError) {
      throw error;
    }

    const message = toErrorMessage(error);
    throw buildStoreFailure(promptKey, targetEnv, provider, model, message, error);
  }
}

export async function resolveTextPrompt(promptKey, options = {}) {
  const prompt = await resolvePromptDefinition(promptKey, {
    ...options,
    expectedContentFormat: options.expectedContentFormat ?? ["text", "text_template"],
  });
  const payload = assertPayloadObject(prompt.payload, promptKey);
  const text = typeof payload.content === "string"
    ? payload.content
    : typeof payload.template === "string"
      ? payload.template
      : "";

  if (!text.trim()) {
    throw new PromptStoreError(`[prompt-store] ${promptKey} 缺少 content/template 文本`, {
      promptKey,
      reason: "missing_text_field",
      contentFormat: prompt.contentFormat,
    });
  }

  return {
    ...prompt,
    text,
  };
}

export async function resolvePromptBundle(promptKey, options = {}) {
  const prompt = await resolvePromptDefinition(promptKey, {
    ...options,
    expectedContentFormat: options.expectedContentFormat ?? ["chat_bundle", "multimodal_bundle", "text_template"],
  });

  return {
    ...prompt,
    payload: assertPayloadObject(prompt.payload, promptKey),
  };
}

export function getRequiredPayloadString(payloadOrPrompt, fieldName, promptKey = "") {
  const payload = payloadOrPrompt && typeof payloadOrPrompt === "object" && "payload" in payloadOrPrompt
    ? payloadOrPrompt.payload
    : payloadOrPrompt;
  const targetPromptKey = promptKey || payloadOrPrompt?.promptKey || "unknown_prompt";
  const objectPayload = assertPayloadObject(payload, targetPromptKey);
  const value = objectPayload[fieldName];

  if (typeof value !== "string" || !value.trim()) {
    throw new PromptStoreError(`[prompt-store] ${targetPromptKey} 缺少字段 ${fieldName}`, {
      promptKey: targetPromptKey,
      reason: "missing_payload_field",
      fieldName,
    });
  }

  return value;
}

export function getOptionalPayloadObject(payloadOrPrompt, fieldName, promptKey = "") {
  const payload = payloadOrPrompt && typeof payloadOrPrompt === "object" && "payload" in payloadOrPrompt
    ? payloadOrPrompt.payload
    : payloadOrPrompt;
  const targetPromptKey = promptKey || payloadOrPrompt?.promptKey || "unknown_prompt";
  const objectPayload = assertPayloadObject(payload, targetPromptKey);
  const value = objectPayload[fieldName];

  return isPlainObject(value) ? value : {};
}