import { checkDeepSeekBalance } from "../providers/deepseek/deepseek-balance.js";
import { forceDisableDeepSeek, getDeepSeekProviderStatus, resolveDeepSeekApiKey, setDeepSeekApiKey, setDeepSeekEnabled, } from "../providers/deepseek/deepseek-config.js";
import { normalizeProviderId, providerDoctorStatus, readProviderRegistry, setProviderDefaults, setProviderConfig, setProviderEnabled, } from "../providers/model-registry.js";
import { toProviderHealth } from "../providers/provider-health.js";
import { maskSensitiveText } from "../util/secret-mask.js";
import { status, label, header, style } from "../util/theme.js";
const PROVIDER_PROFILES = [
    {
        id: "openai-api",
        provider: "custom",
        profileType: "runtime",
        planKind: "openai-api",
        wireApi: "openai-chat-completions",
        authMethod: "api-key-env",
        routing: "advisory",
        authority: "read-only",
        credentialOwner: "omk-env",
        notes: ["OpenAI-compatible custom providers are advisory/read-only unless Kimi performs the final action."],
    },
    {
        id: "codex-chatgpt-plan",
        provider: "codex",
        profileType: "compatibility",
        planKind: "chatgpt-plan",
        wireApi: "external-cli",
        authMethod: "external-cli",
        routing: "external-cli",
        authority: "advisory",
        credentialOwner: "official-cli",
        notes: ["Use the official Codex CLI login; OMK does not read ~/.codex auth JSON."],
    },
    {
        id: "claude-code-plan",
        provider: "claude",
        profileType: "compatibility",
        planKind: "claude-code-plan",
        wireApi: "external-cli",
        authMethod: "external-cli",
        routing: "external-cli",
        authority: "advisory",
        credentialOwner: "official-cli",
        notes: ["Use the official Claude Code CLI login; OMK does not copy subscription credentials."],
    },
    {
        id: "gemini-cli-plan",
        provider: "gemini",
        profileType: "compatibility",
        planKind: "gemini-cli-plan",
        wireApi: "external-cli",
        authMethod: "external-cli",
        routing: "external-cli",
        authority: "advisory",
        credentialOwner: "official-cli",
        notes: ["Use Gemini CLI Google login or documented environment variables; OMK does not scrape token files."],
    },
    {
        id: "qwen-coding-plan",
        provider: "qwen",
        profileType: "runtime",
        planKind: "qwen-coding-plan",
        wireApi: "openai-chat-completions",
        authMethod: "api-key-env",
        routing: "advisory",
        authority: "read-only",
        credentialOwner: "omk-env",
        notes: ["Qwen/DashScope runs through OpenAI-compatible advisory lanes by env-var reference only."],
    },
    {
        id: "openrouter-credits",
        provider: "openrouter",
        profileType: "runtime",
        planKind: "openrouter-credits",
        wireApi: "openai-chat-completions",
        authMethod: "api-key-env",
        routing: "advisory",
        authority: "read-only",
        credentialOwner: "provider",
        notes: ["Use OPENROUTER_API_KEY; model costs are controlled on OpenRouter."],
    },
    {
        id: "openrouter-byok",
        provider: "openrouter",
        profileType: "runtime",
        planKind: "openrouter-byok",
        wireApi: "openai-chat-completions",
        authMethod: "api-key-env",
        routing: "advisory",
        authority: "read-only",
        credentialOwner: "provider",
        notes: ["Use OpenRouter provider-side BYOK settings; OMK still only sees the OPENROUTER_API_KEY env reference."],
    },
];
export async function providerDoctorCommand(provider, options = {}) {
    const target = provider ?? "deepseek";
    if (target !== "deepseek") {
        const normalized = normalizeProviderId(target);
        const payload = await providerDoctorStatus(normalized === "auto" ? "kimi" : normalized);
        if (options.json) {
            console.log(JSON.stringify({ ...payload, health: toProviderHealth(payload) }, null, 2));
        }
        else {
            console.log(header("Provider doctor"));
            console.log(label("Provider", payload.provider));
            console.log(label("Kind", payload.kind));
            console.log(label("Model", payload.model));
            if (payload.baseUrl)
                console.log(label("Base URL", payload.baseUrl));
            if (payload.apiKeyEnv)
                console.log(label("API key env", payload.apiKeySet ? `${payload.apiKeyEnv} (set)` : `${payload.apiKeyEnv} (missing)`));
            console.log(payload.available ? status.ok("Provider available") : status.warn(payload.reason ?? "Provider unavailable"));
            console.log(style.gray("Fallback: Kimi remains the final authority."));
        }
        if (!payload.available && !options.soft)
            process.exitCode = 1;
        return;
    }
    const providerStatus = await getDeepSeekProviderStatus();
    const key = await resolveDeepSeekApiKey();
    const result = providerStatus.enabled
        ? await checkDeepSeekBalance({ apiKey: key.apiKey })
        : {
            provider: "deepseek",
            available: false,
            checkedAt: Date.now(),
            reason: providerStatus.disabledReason ?? "DeepSeek is disabled",
            disableForRun: true,
        };
    if (options.json) {
        const deepseekDoctor = {
            ...result,
            enabled: providerStatus.enabled,
            disabledBy: providerStatus.disabledBy,
            apiKeySet: providerStatus.apiKeySet,
            apiKeySource: providerStatus.apiKeySource,
        };
        console.log(JSON.stringify({ ...deepseekDoctor, health: toProviderHealth(deepseekDoctor) }, null, 2));
    }
    else {
        console.log(header("Provider doctor"));
        console.log(label("Provider", "deepseek"));
        console.log(label("Mode", "opportunistic read-only worker"));
        console.log(label("Enabled", providerStatus.enabled ? "yes" : "no"));
        console.log(label("API key", providerStatus.apiKeySet ? `set (${providerStatus.apiKeySource ?? "unknown"})` : "missing"));
        if (result.available) {
            console.log(status.ok("DeepSeek is available"));
            const balances = result.balance?.balance_infos ?? [];
            for (const balance of balances) {
                console.log(label(`Balance ${balance.currency}`, balance.total_balance));
            }
        }
        else {
            console.log(status.error(result.reason ?? "DeepSeek unavailable"));
            if (!providerStatus.enabled) {
                console.log(style.gray("Run /deepseek-enable or `omk deepseek enable` after fixing the issue."));
            }
            console.log(style.gray("Fallback: Kimi remains the primary provider for all nodes."));
        }
    }
    if (!result.available && !options.soft) {
        process.exitCode = 1;
    }
}
export async function providerListCommand(options = {}) {
    const providers = await readProviderRegistry();
    const payload = {
        providers: providers.map((entry) => ({
            provider: entry.id,
            enabled: entry.enabled,
            kind: entry.kind,
            baseUrl: entry.baseUrl,
            apiKeyEnv: entry.apiKeyEnv,
            defaultModel: entry.defaultModel,
            aliases: entry.aliases,
            capabilities: entry.capabilities,
            wireApi: entry.wireApi,
            authMethod: entry.auth?.method,
            profileType: entry.profileType,
            planKind: entry.planKind,
            routing: entry.routing,
            configured: entry.configured,
            disabledReason: entry.disabledReason,
        })),
    };
    if (options.json) {
        console.log(JSON.stringify(payload, null, 2));
        return;
    }
    console.log(header("Provider registry"));
    for (const provider of payload.providers) {
        console.log(`${provider.enabled ? status.ok("enabled") : status.warn("disabled")} ${provider.provider} ${style.gray(provider.kind)} ${provider.defaultModel}`);
        if (provider.apiKeyEnv)
            console.log(`  ${label("API key env", provider.apiKeyEnv)}`);
        if (provider.baseUrl)
            console.log(`  ${label("Base URL", provider.baseUrl)}`);
    }
}
export async function providerSetCommand(provider, options = {}) {
    const normalized = normalizeProviderId(provider);
    if (normalized === "auto")
        throw new Error("Provider id is required");
    const entry = await setProviderConfig(normalized, {
        model: options.model,
        baseUrl: options.baseUrl,
        apiKeyEnv: options.apiKeyEnv,
        kind: options.kind,
        authMethod: options.authMethod ? normalizeAuthMethodOption(options.authMethod) : undefined,
        enabled: true,
    });
    const payload = {
        provider: entry.id,
        enabled: entry.enabled,
        kind: entry.kind,
        baseUrl: entry.baseUrl,
        apiKeyEnv: entry.apiKeyEnv,
        defaultModel: entry.defaultModel,
        capabilities: entry.capabilities,
        wireApi: entry.wireApi,
        authMethod: entry.auth?.method,
        profileType: entry.profileType,
        planKind: entry.planKind,
        routing: entry.routing,
    };
    emitProviderMutation("Provider configured", payload, options);
}
export async function providerUseCommand(provider, options = {}) {
    const normalized = normalizeProviderId(provider);
    if (normalized === "auto")
        throw new Error("Provider id is required");
    const entry = await setProviderConfig(normalized, {
        model: options.model,
        enabled: true,
    });
    const defaults = await setProviderDefaults({
        provider: entry.id,
        model: options.model ?? entry.defaultModel,
    });
    emitProviderMutation("Provider default updated", {
        ok: true,
        command: "provider use",
        provider: entry.id,
        defaultProvider: defaults.defaults.provider,
        defaultModel: defaults.defaults.model,
        authority: options.authority === true,
        configPath: defaults.configPath,
        secretValuesPrinted: false,
        tokenFilesRead: false,
    }, options);
}
export async function providerAuthCommand(provider, options = {}) {
    const normalized = normalizeProviderId(provider);
    if (normalized === "auto")
        throw new Error("Provider id is required");
    const authMethod = normalizeAuthMethodOption(options.method);
    const apiKeyEnv = authMethod === "api-key-env" || authMethod === "oauth"
        ? normalizeOptionalApiKeyEnv(options.apiKeyEnv ?? defaultApiKeyEnvForProvider(normalized))
        : undefined;
    const entry = await setProviderConfig(normalized, {
        apiKeyEnv,
        authMethod,
        kind: providerKindForAuth(normalized, authMethod),
        enabled: true,
    });
    emitProviderMutation("Provider auth configured", {
        ok: true,
        command: "provider auth",
        provider: entry.id,
        authMethod: entry.auth?.method ?? authMethod,
        apiKeyEnv: authMethod === "api-key-env" || authMethod === "oauth" ? entry.apiKeyEnv : undefined,
        wireApi: entry.wireApi,
        profileType: entry.profileType,
        planKind: entry.planKind,
        routing: entry.routing,
        secretValuesPrinted: false,
        tokenFilesRead: false,
        projectFilesWritten: false,
    }, options);
}
export async function providerProfilesCommand(options = {}) {
    const payload = {
        ok: true,
        command: "provider profiles",
        profiles: PROVIDER_PROFILES,
        secretValuesPrinted: false,
        tokenFilesRead: false,
    };
    if (options.json) {
        console.log(JSON.stringify(payload, null, 2));
        return;
    }
    console.log(header("Provider profiles"));
    for (const profile of PROVIDER_PROFILES) {
        console.log(`${status.ok(profile.id)} ${style.gray(profile.provider)} ${profile.planKind}`);
        console.log(`  ${label("Auth", profile.authMethod)}`);
        console.log(`  ${label("Authority", profile.authority)}`);
    }
}
export async function providerEnableCommand(provider, options = {}) {
    const normalized = normalizeProviderId(provider);
    if (normalized === "auto")
        throw new Error("Provider id is required");
    const entry = await setProviderEnabled(normalized, true);
    emitProviderMutation("Provider enabled", {
        provider: entry.id,
        enabled: entry.enabled,
        kind: entry.kind,
        apiKeyEnv: entry.apiKeyEnv,
        defaultModel: entry.defaultModel,
    }, options);
}
export async function providerDisableCommand(provider, reason = "Disabled by user", options = {}) {
    const normalized = normalizeProviderId(provider);
    if (normalized === "auto")
        throw new Error("Provider id is required");
    const entry = normalized === "deepseek"
        ? await forceDisableDeepSeek(reason, { disabledBy: "user" }).then(() => setProviderEnabled("deepseek", false, reason))
        : await setProviderEnabled(normalized, false, reason);
    emitProviderMutation("Provider disabled", {
        provider: entry.id,
        enabled: entry.enabled,
        disabledReason: entry.disabledReason ?? reason,
    }, options);
}
export async function providerOAuthCommand(provider, options = {}) {
    const payload = buildProviderOAuthResult(provider, options);
    if (options.json) {
        console.log(JSON.stringify(payload, null, 2));
        return;
    }
    console.log(header("Provider OAuth"));
    console.log(label("Provider", payload.provider));
    console.log(label("Auth method", payload.authMethod));
    console.log(label("OAuth exchange", payload.oauthAvailable ? "provider-managed" : "not handled by OMK"));
    console.log(label("Exchange performed", "no"));
    console.log(label("Project files written", "no"));
    console.log(label("Secrets printed", "no"));
    for (const action of payload.nextActions) {
        console.log(`  ${style.gray("•")} ${action}`);
    }
    for (const note of payload.notes) {
        console.log(style.gray(note));
    }
}
export async function providerDeepSeekEnableCommand(options = {}) {
    const config = await setDeepSeekEnabled(true);
    const payload = {
        provider: "deepseek",
        enabled: config.enabled !== false,
        message: "DeepSeek opportunistic workers enabled",
    };
    if (options.json) {
        console.log(JSON.stringify(payload, null, 2));
    }
    else {
        console.log(header("DeepSeek provider"));
        console.log(status.ok(payload.message));
        console.log(style.gray("Kimi remains the orchestrator; DeepSeek is used only for safe read/review/QA/documentation nodes."));
    }
}
export async function providerDeepSeekDisableCommand(reason = "Disabled by user", options = {}) {
    const config = await forceDisableDeepSeek(reason, { disabledBy: "user" });
    const payload = {
        provider: "deepseek",
        enabled: config.enabled !== false,
        disabledReason: config.disabledReason,
        disabledBy: config.disabledBy,
    };
    if (options.json) {
        console.log(JSON.stringify(payload, null, 2));
    }
    else {
        console.log(header("DeepSeek provider"));
        console.log(status.warn("DeepSeek forced disabled"));
        console.log(label("Reason", config.disabledReason ?? reason));
        console.log(style.gray("Configured authority fallback remains active."));
    }
}
export async function providerDeepSeekSetCommand(options = {}) {
    const input = await resolveApiKeyInput({ ...options, promptWhenTty: true });
    await saveDeepSeekApiKey(input, options);
}
export async function providerDeepSeekApiCommand(options = {}) {
    const input = await resolveApiKeyInput({
        fromEnv: options.fromEnv,
        promptWhenTty: true,
    });
    await saveDeepSeekApiKey(input, options);
}
async function resolveApiKeyInput(options) {
    if (options.fromEnv) {
        const value = process.env[options.fromEnv];
        if (!value)
            throw new Error(`Environment variable is not set: ${options.fromEnv}`);
        return { apiKey: value, source: "env" };
    }
    if (process.env.DEEPSEEK_API_KEY) {
        return { apiKey: process.env.DEEPSEEK_API_KEY, source: "env" };
    }
    if (!process.stdin.isTTY) {
        const stdin = await readStdin();
        if (stdin.trim())
            return { apiKey: stdin.trim(), source: "stdin" };
    }
    if (options.promptWhenTty && process.stdin.isTTY && process.stdout.isTTY) {
        const { password } = await import("@inquirer/prompts");
        const apiKey = await password({
            message: "DeepSeek API key",
            mask: "*",
            validate: (value) => value.trim().length > 0 || "DeepSeek API key is required",
        });
        return { apiKey, source: "prompt" };
    }
    throw new Error("DeepSeek API key is required. Use `omk deepseek api --from-env DEEPSEEK_API_KEY` or pipe the key via stdin.");
}
async function saveDeepSeekApiKey(input, options = {}) {
    const result = await setDeepSeekApiKey(input.apiKey);
    const payload = {
        provider: "deepseek",
        enabled: true,
        apiKeySet: true,
        apiKeyEnv: result.apiKeyEnv,
        secretsPath: result.secretsPath,
        source: input.source,
    };
    if (options.json) {
        console.log(JSON.stringify(payload, null, 2));
    }
    else {
        console.log(header("DeepSeek provider"));
        console.log(status.ok("DeepSeek API key saved without printing it"));
        console.log(label("Secret file", result.secretsPath));
        console.log(label("Env", result.apiKeyEnv));
        console.log(style.gray("DeepSeek hybrid routing has been enabled. Run `omk deepseek doctor` to verify balance."));
    }
}
function emitProviderMutation(title, payload, options) {
    if (options.json) {
        console.log(JSON.stringify(payload, null, 2));
        return;
    }
    console.log(header(title));
    console.log(status.ok(title));
    for (const [key, value] of Object.entries(payload)) {
        if (value === undefined)
            continue;
        console.log(label(key, typeof value === "string" ? value : JSON.stringify(value)));
    }
    console.log(style.gray("Secrets are referenced by env var name only; values are never printed."));
}
export function buildProviderOAuthResult(provider, options = {}) {
    const target = safeProviderId(provider);
    const apiKeyEnv = normalizeOptionalApiKeyEnv(options.apiKeyEnv ?? defaultApiKeyEnvForProvider(target));
    const commonNotes = [
        "OMK does not read OAuth token files, print token values, or write provider secrets into project files.",
        "Kimi remains available as an explicit API provider; OMK keeps provider-neutral authority by default.",
    ];
    if (target === "kimi") {
        return {
            ok: true,
            command: "provider oauth",
            provider: target,
            authMethod: "api-key-env",
            oauthAvailable: false,
            exchangeRequiresBrowser: false,
            exchangePerformed: false,
            authBypass: false,
            authJsonRead: false,
            tokenFilesRead: false,
            secretValuesPrinted: false,
            secretsStored: false,
            projectFilesWritten: false,
            tokensRead: false,
            checkedAt: new Date().toISOString(),
            nextActions: [
                "Set KIMI_API_KEY in your shell or provider environment.",
                "Optional: set KIMI_MODEL or configure [providers.kimi] in ~/.omk/config.toml.",
                "Run `omk provider doctor kimi --soft` to verify OMK sees the Kimi API credentials.",
            ],
            notes: commonNotes,
        };
    }
    if (target === "codex") {
        return {
            ok: true,
            command: "provider oauth",
            provider: target,
            authMethod: "official-cli-oauth",
            oauthAvailable: true,
            exchangeRequiresBrowser: true,
            exchangePerformed: false,
            authBypass: false,
            authJsonRead: false,
            tokenFilesRead: false,
            secretValuesPrinted: false,
            secretsStored: false,
            projectFilesWritten: false,
            tokensRead: false,
            checkedAt: new Date().toISOString(),
            nextActions: [
                "Run `codex login` or `codex login --device-auth` in a local terminal to complete the official flow.",
                "Run `omk provider doctor codex --soft` to verify CLI availability; OMK still does not read ~/.codex/auth.json tokens.",
                "Do not reuse Codex/ChatGPT OAuth tokens as OpenAI-compatible API keys.",
            ],
            notes: commonNotes,
        };
    }
    if (target === "openrouter") {
        return {
            ok: true,
            command: "provider oauth",
            provider: target,
            authMethod: "oauth",
            oauthAvailable: true,
            exchangeRequiresBrowser: true,
            exchangePerformed: false,
            authBypass: false,
            authJsonRead: false,
            tokenFilesRead: false,
            secretValuesPrinted: false,
            secretsStored: false,
            projectFilesWritten: false,
            tokensRead: false,
            apiKeyEnv,
            checkedAt: new Date().toISOString(),
            nextActions: [
                "Complete OpenRouter's browser PKCE flow in a local trusted environment.",
                "Exchange the authorization code through OpenRouter's documented /api/v1/auth/keys flow without printing the returned key.",
                `Store the resulting key in a user-local secret manager or shell as ${apiKeyEnv}.`,
                `Register only metadata with \`omk provider auth openrouter --method oauth --api-key-env ${apiKeyEnv} --json\`.`,
                "Run `omk provider doctor openrouter --soft` after exporting the environment variable.",
            ],
            notes: [
                ...commonNotes,
                "OpenRouter is OpenAI-compatible at https://openrouter.ai/api/v1; OMK routes it as read-only/advisory.",
            ],
        };
    }
    const providerName = target === "deepseek"
        ? "DeepSeek"
        : target === "qwen"
            ? "Qwen/DashScope"
            : target;
    const setCommand = `omk provider set ${target} --api-key-env ${apiKeyEnv}`;
    const apiKeySetup = target === "deepseek"
        ? `For user-local DeepSeek secret storage, use \`omk deepseek api --from-env ${apiKeyEnv}\`; this command does not do that storage.`
        : `Set ${apiKeyEnv} in your shell or secret manager for the one process that needs it.`;
    return {
        ok: true,
        command: "provider oauth",
        provider: target,
        authMethod: target === "custom" ? "external-provider" : "api-key-env",
        oauthAvailable: false,
        exchangeRequiresBrowser: false,
        exchangePerformed: false,
        authBypass: false,
        authJsonRead: false,
        tokenFilesRead: false,
        secretValuesPrinted: false,
        secretsStored: false,
        projectFilesWritten: false,
        tokensRead: false,
        apiKeyEnv,
        checkedAt: new Date().toISOString(),
        nextActions: [
            `${providerName} does not have an OMK-managed OAuth exchange; use provider docs or API-key environment variables.`,
            `Register only the environment variable name with \`${setCommand}\`.`,
            apiKeySetup,
            `Run \`omk provider doctor ${target} --soft\` after setting the environment variable.`,
        ],
        notes: commonNotes,
    };
}
function safeProviderId(value) {
    const normalized = normalizeProviderId(value ?? "codex");
    if (normalized === "auto")
        return "codex";
    const masked = maskSensitiveText(normalized);
    if (masked !== normalized || !/^[a-z][a-z0-9._-]{0,63}$/u.test(normalized)) {
        return "custom";
    }
    return normalized;
}
function normalizeOptionalApiKeyEnv(value) {
    if (value === undefined)
        return undefined;
    const trimmed = value.trim();
    if (/^[A-Za-z_][A-Za-z0-9_]*$/u.test(trimmed))
        return trimmed;
    throw new Error("Provider API key env must be an environment variable name, not a secret value");
}
function defaultApiKeyEnvForProvider(provider) {
    if (provider === "deepseek")
        return "DEEPSEEK_API_KEY";
    if (provider === "openrouter")
        return "OPENROUTER_API_KEY";
    if (provider === "qwen")
        return "DASHSCOPE_API_KEY";
    return "PROVIDER_API_KEY";
}
function normalizeAuthMethodOption(value) {
    if (!value)
        return "api-key-env";
    if (value === "api-key-env" || value === "oauth" || value === "external-cli" || value === "none")
        return value;
    throw new Error("Provider auth method must be api-key-env, oauth, external-cli, or none");
}
function providerKindForAuth(provider, authMethod) {
    if (provider === "kimi")
        return "openai-compatible";
    if (provider === "codex")
        return "codex-cli";
    if (authMethod === "external-cli")
        return "external-cli";
    return "openai-compatible";
}
async function readStdin() {
    const chunks = [];
    for await (const chunk of process.stdin) {
        chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
    }
    return Buffer.concat(chunks).toString("utf-8");
}
