import { chmod, mkdir, readFile, writeFile } from "fs/promises";
import { dirname, join, posix } from "path";
import { getUserHome } from "../../util/fs.js";
const DEFAULT_API_KEY_ENV = "DEEPSEEK_API_KEY";
export function getDeepSeekProviderConfigPath(options = {}) {
    const env = options.env ?? process.env;
    return env.OMK_PROVIDER_CONFIG_PATH ?? joinHomePath(resolveHomeDir(options), ".config", "omk", "providers.json");
}
export function getOmkSecretsEnvPath(options = {}) {
    const env = options.env ?? process.env;
    return env.OMK_SECRETS_ENV_PATH ?? joinHomePath(resolveHomeDir(options), ".config", "omk", "secrets.env");
}
export function getOpenCodeSecretsEnvPath(options = {}) {
    const env = options.env ?? process.env;
    return env.OPENCODE_SECRETS_ENV_PATH ?? joinHomePath(resolveHomeDir(options), ".config", "opencode", "secrets.env");
}
export async function readOmkProvidersConfig(options = {}) {
    const path = getDeepSeekProviderConfigPath(options);
    const raw = await readFile(path, "utf-8").catch(() => null);
    if (!raw)
        return emptyProvidersConfig();
    try {
        const parsed = JSON.parse(raw);
        return {
            version: 1,
            providers: normalizeProvidersConfig(parsed.providers),
        };
    }
    catch {
        return emptyProvidersConfig();
    }
}
export async function writeOmkProvidersConfig(config, options = {}) {
    const path = getDeepSeekProviderConfigPath(options);
    await mkdir(dirname(path), { recursive: true });
    await writeFile(path, JSON.stringify(config, null, 2) + "\n", { mode: 0o600 });
    await chmod(path, 0o600).catch(() => undefined);
    return path;
}
export async function setDeepSeekEnabled(enabled, options = {}) {
    const config = await readOmkProvidersConfig(options);
    const current = normalizeDeepSeekProviderConfig(config.providers.deepseek);
    const updatedAt = new Date().toISOString();
    const next = enabled
        ? {
            ...current,
            enabled: true,
            disabledAt: undefined,
            disabledReason: undefined,
            disabledBy: undefined,
            updatedAt,
        }
        : {
            ...current,
            enabled: false,
            disabledAt: updatedAt,
            disabledReason: options.reason ?? "Disabled by user",
            disabledBy: options.disabledBy ?? "user",
            updatedAt,
        };
    await writeOmkProvidersConfig({ version: 1, providers: { ...config.providers, deepseek: next } }, options);
    return next;
}
export async function forceDisableDeepSeek(reason, options = {}) {
    const disabledBy = options.disabledBy ?? (reason.includes("402") ? "provider-402" : "provider-availability");
    return setDeepSeekEnabled(false, { ...options, reason, disabledBy });
}
export async function setDeepSeekApiKey(apiKey, options = {}) {
    const value = apiKey.trim();
    if (!value)
        throw new Error("DeepSeek API key is empty");
    if (/[\r\n\0]/.test(value))
        throw new Error("DeepSeek API key must be a single line");
    const apiKeyEnv = options.apiKeyEnv ?? DEFAULT_API_KEY_ENV;
    const secretsPath = getOmkSecretsEnvPath(options);
    await upsertEnvFileValue(secretsPath, apiKeyEnv, value);
    const config = await readOmkProvidersConfig(options);
    const deepseek = {
        ...normalizeDeepSeekProviderConfig(config.providers.deepseek),
        enabled: true,
        apiKeyEnv,
        disabledAt: undefined,
        disabledReason: undefined,
        disabledBy: undefined,
        updatedAt: new Date().toISOString(),
    };
    await writeOmkProvidersConfig({ version: 1, providers: { ...config.providers, deepseek } }, options);
    return { secretsPath, apiKeyEnv };
}
export async function resolveDeepSeekApiKey(options = {}) {
    const config = await readOmkProvidersConfig(options);
    const apiKeyEnv = options.apiKeyEnv ?? config.providers.deepseek?.apiKeyEnv ?? DEFAULT_API_KEY_ENV;
    const env = options.env ?? process.env;
    const envValue = env[apiKeyEnv]?.trim();
    if (envValue)
        return { apiKey: envValue, apiKeyEnv, source: "env" };
    const omkSecretsPath = getOmkSecretsEnvPath(options);
    const omkValue = await readEnvFileValue(omkSecretsPath, apiKeyEnv);
    if (omkValue)
        return { apiKey: omkValue, apiKeyEnv, source: "omk-secrets", sourcePath: omkSecretsPath };
    const opencodeSecretsPath = getOpenCodeSecretsEnvPath(options);
    const opencodeValue = await readEnvFileValue(opencodeSecretsPath, apiKeyEnv);
    if (opencodeValue) {
        return { apiKey: opencodeValue, apiKeyEnv, source: "opencode-secrets", sourcePath: opencodeSecretsPath };
    }
    return { apiKeyEnv };
}
export async function getDeepSeekProviderStatus(options = {}) {
    const [config, key] = await Promise.all([
        readOmkProvidersConfig(options),
        resolveDeepSeekApiKey(options),
    ]);
    const deepseek = normalizeDeepSeekProviderConfig(config.providers.deepseek);
    const enabled = deepseek.enabled !== false;
    return {
        provider: "deepseek",
        enabled,
        disabledReason: deepseek.disabledReason,
        disabledBy: isDeepSeekDisabledBy(deepseek.disabledBy) ? deepseek.disabledBy : undefined,
        apiKeySet: Boolean(key.apiKey),
        apiKeySource: key.source,
        configPath: getDeepSeekProviderConfigPath(options),
        secretsPath: getOmkSecretsEnvPath(options),
    };
}
function resolveHomeDir(options) {
    return options.homeDir ?? getUserHome(options.env ?? process.env);
}
function joinHomePath(homeDir, ...parts) {
    return isPosixStyleHome(homeDir) ? posix.join(homeDir, ...parts) : join(homeDir, ...parts);
}
function isPosixStyleHome(homeDir) {
    return homeDir.startsWith("/") && !homeDir.startsWith("//") && !homeDir.includes("\\");
}
function emptyProvidersConfig() {
    return {
        version: 1,
        providers: {},
    };
}
function normalizeProvidersConfig(value) {
    if (!isRecord(value))
        return {};
    const providers = {};
    for (const [id, raw] of Object.entries(value)) {
        if (id === "deepseek") {
            providers[id] = normalizeDeepSeekProviderConfig(raw);
        }
        else {
            providers[id] = normalizeGenericProviderConfig(raw);
        }
    }
    return providers;
}
function normalizeDeepSeekProviderConfig(value) {
    if (!isRecord(value))
        return {};
    return {
        enabled: typeof value.enabled === "boolean" ? value.enabled : undefined,
        disabledAt: typeof value.disabledAt === "string" ? value.disabledAt : undefined,
        disabledReason: typeof value.disabledReason === "string" ? value.disabledReason : undefined,
        disabledBy: typeof value.disabledBy === "string" && value.disabledBy.trim() ? value.disabledBy : undefined,
        updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : undefined,
        apiKeyEnv: typeof value.apiKeyEnv === "string" && value.apiKeyEnv.trim() ? value.apiKeyEnv : undefined,
        model: typeof value.model === "string" && value.model.trim() ? value.model : undefined,
        kind: value.kind === "openai-compatible" ? value.kind : undefined,
        baseUrl: typeof value.baseUrl === "string" && value.baseUrl.trim() ? value.baseUrl : undefined,
        defaultModel: typeof value.defaultModel === "string" && value.defaultModel.trim() ? value.defaultModel : undefined,
        aliases: normalizeStringMap(value.aliases),
        capabilities: normalizeStringList(value.capabilities),
        contextWindow: normalizePositiveInteger(value.contextWindow),
        reservedOutputTokens: normalizePositiveInteger(value.reservedOutputTokens),
    };
}
function normalizeGenericProviderConfig(value) {
    if (!isRecord(value))
        return {};
    return {
        enabled: typeof value.enabled === "boolean" ? value.enabled : undefined,
        kind: isProviderKind(value.kind) ? value.kind : undefined,
        baseUrl: typeof value.baseUrl === "string" && value.baseUrl.trim() ? value.baseUrl : undefined,
        apiKeyEnv: typeof value.apiKeyEnv === "string" && value.apiKeyEnv.trim() ? value.apiKeyEnv : undefined,
        defaultModel: typeof value.defaultModel === "string" && value.defaultModel.trim() ? value.defaultModel : undefined,
        model: typeof value.model === "string" && value.model.trim() ? value.model : undefined,
        aliases: normalizeStringMap(value.aliases),
        capabilities: normalizeStringList(value.capabilities),
        contextWindow: normalizePositiveInteger(value.contextWindow),
        reservedOutputTokens: normalizePositiveInteger(value.reservedOutputTokens),
        disabledAt: typeof value.disabledAt === "string" ? value.disabledAt : undefined,
        disabledReason: typeof value.disabledReason === "string" ? value.disabledReason : undefined,
        disabledBy: typeof value.disabledBy === "string" && value.disabledBy.trim() ? value.disabledBy : undefined,
        updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : undefined,
    };
}
function normalizeStringMap(value) {
    if (!isRecord(value))
        return undefined;
    const entries = Object.entries(value)
        .filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0);
    return entries.length > 0 ? Object.fromEntries(entries) : undefined;
}
function normalizeStringList(value) {
    if (!Array.isArray(value))
        return undefined;
    const list = value.filter((item) => typeof item === "string" && item.trim().length > 0)
        .map((item) => item.trim());
    return list.length > 0 ? [...new Set(list)] : undefined;
}
function normalizePositiveInteger(value) {
    return typeof value === "number" && Number.isFinite(value) && value > 0
        ? Math.floor(value)
        : undefined;
}
function isRecord(value) {
    return typeof value === "object" && value !== null;
}
function isDeepSeekDisabledBy(value) {
    return value === "user" || value === "provider-402" || value === "provider-availability";
}
function isProviderKind(value) {
    return value === "kimi-native" || value === "openai-compatible" || value === "codex-cli" || value === "local";
}
async function upsertEnvFileValue(path, name, value) {
    const existing = await readFile(path, "utf-8").catch(() => "");
    const linePattern = new RegExp(`^\\s*(?:export\\s+)?${escapeRegExp(name)}\\s*=`);
    const nextLine = `export ${name}=${shellSingleQuote(value)}`;
    let replaced = false;
    const lines = existing.split(/\r?\n/).filter((line, index, all) => !(index === all.length - 1 && line === ""));
    const nextLines = lines.map((line) => {
        if (linePattern.test(line)) {
            replaced = true;
            return nextLine;
        }
        return line;
    });
    if (!replaced)
        nextLines.push(nextLine);
    await mkdir(dirname(path), { recursive: true });
    await writeFile(path, nextLines.join("\n") + "\n", { mode: 0o600 });
    await chmod(path, 0o600).catch(() => undefined);
}
async function readEnvFileValue(path, name) {
    const raw = await readFile(path, "utf-8").catch(() => null);
    if (!raw)
        return undefined;
    const linePattern = new RegExp(`^\\s*(?:export\\s+)?${escapeRegExp(name)}\\s*=\\s*(.*)\\s*$`);
    for (const line of raw.split(/\r?\n/)) {
        const match = line.match(linePattern);
        if (!match)
            continue;
        const parsed = parseEnvValue(match[1] ?? "");
        if (parsed.trim())
            return parsed.trim();
    }
    return undefined;
}
function parseEnvValue(raw) {
    const value = raw.trim();
    if (value.startsWith("'") && value.endsWith("'")) {
        return value.slice(1, -1).replace(/'\\''/g, "'");
    }
    if (value.startsWith('"') && value.endsWith('"')) {
        return value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
    }
    return value.replace(/\s+#.*$/, "").trim();
}
function shellSingleQuote(value) {
    return `'${value.replace(/'/g, "'\\''")}'`;
}
function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
