import chalk from 'chalk';
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
import { homedir } from 'os';
import path from 'path';
import readline from 'readline';
import { Config } from '../utils/config.js';
const DEFAULT_API_URL = 'https://coder.vigthoria.io';
const CONFIG_DIR = path.join(homedir(), '.vigthoria');
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
const KNOWN_AUTH_BASE_URLS = ['https://coder.vigthoria.io'];
class HttpError extends Error {
    status;
    constructor(status, message) {
        super(message);
        this.status = status;
    }
}
function trimTrailingSlash(value) {
    return value.replace(/\/+$/, '');
}
function uniqueStrings(values) {
    return [...new Set(values.filter(Boolean))];
}
function getApiUrl() {
    return trimTrailingSlash(process.env.VIGTHORIA_API_URL || DEFAULT_API_URL);
}
function derivePeerHost(baseUrl) {
    if (baseUrl.includes('://api.vigthoria.io')) {
        return baseUrl.replace('://api.vigthoria.io', '://coder.vigthoria.io');
    }
    if (baseUrl.includes('://coder.vigthoria.io')) {
        return baseUrl.replace('://coder.vigthoria.io', '://api.vigthoria.io');
    }
    return undefined;
}
function getAuthBaseCandidates(seedBaseUrl) {
    const seed = trimTrailingSlash(seedBaseUrl || getApiUrl());
    const peer = derivePeerHost(seed);
    return uniqueStrings([seed, ...(peer ? [peer] : []), ...KNOWN_AUTH_BASE_URLS]).map(trimTrailingSlash);
}
function ensureConfigDir() {
    if (!existsSync(CONFIG_DIR)) {
        mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
    }
    try {
        chmodSync(CONFIG_DIR, 0o700);
    }
    catch {
        // Best-effort on non-POSIX filesystems.
    }
}
function syncSharedConfig(config) {
    try {
        const shared = new Config();
        shared.set('apiUrl', trimTrailingSlash(config.apiUrl || getApiUrl()));
        if (config.token) {
            shared.set('authToken', config.token);
        }
        if (config.user?.id) {
            shared.set('userId', String(config.user.id));
        }
        if (config.user?.email) {
            shared.set('email', String(config.user.email));
        }
        if (config.v3ServiceKey) {
            shared.set('v3ServiceKey', config.v3ServiceKey);
        }
    }
    catch {
        // Keep legacy auth flow working even if shared config write fails.
    }
}
function clearSharedConfigAuth() {
    try {
        const shared = new Config();
        shared.set('authToken', null);
        shared.set('refreshToken', null);
        shared.set('userId', null);
        shared.set('email', null);
        shared.set('v3ServiceKey', null);
    }
    catch {
        // Ignore shared config clear failures during logout.
    }
}
function humanMessage(error) {
    const raw = error instanceof Error ? error.message : String(error);
    if (/^\s*</.test(raw) || /<!doctype html/i.test(raw)) {
        return 'The Vigthoria service returned an unexpected HTML response. This usually means auth routes are misconfigured server-side.';
    }
    return raw || 'Unknown authentication error.';
}
function extractAuthToken(payload) {
    if (!payload || typeof payload !== 'object') {
        return undefined;
    }
    const body = payload;
    return body.token || body.accessToken || body.tokens?.access_token;
}
function extractAuthUser(payload, fallbackEmail) {
    if (!payload || typeof payload !== 'object') {
        return fallbackEmail ? { email: fallbackEmail } : undefined;
    }
    const body = payload;
    const user = body.user;
    if (user && typeof user === 'object') {
        const typed = user;
        return {
            id: typed.id ? String(typed.id) : undefined,
            email: typed.email ? String(typed.email) : fallbackEmail,
            name: typed.name ? String(typed.name) : typed.username ? String(typed.username) : undefined,
        };
    }
    if (body.email || body.id || body.username) {
        return {
            id: body.id ? String(body.id) : undefined,
            email: body.email ? String(body.email) : fallbackEmail,
            name: body.username ? String(body.username) : body.name ? String(body.name) : undefined,
        };
    }
    return fallbackEmail ? { email: fallbackEmail } : undefined;
}
export function loadAuthConfig() {
    if (!existsSync(CONFIG_FILE)) {
        return { apiUrl: getApiUrl() };
    }
    try {
        const parsed = JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
        return {
            apiUrl: trimTrailingSlash(parsed.apiUrl || getApiUrl()),
            token: parsed.token || parsed.authToken,
            v3ServiceKey: typeof parsed.v3ServiceKey === 'string' ? parsed.v3ServiceKey : undefined,
            user: parsed.user,
        };
    }
    catch (error) {
        console.warn(chalk.yellow(`Warning: could not read auth config: ${humanMessage(error)}`));
        return { apiUrl: getApiUrl() };
    }
}
export function saveAuthConfig(config) {
    ensureConfigDir();
    writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
    try {
        chmodSync(CONFIG_FILE, 0o600);
    }
    catch {
        // Best-effort on non-POSIX filesystems.
    }
    syncSharedConfig(config);
}
export function clearAuthConfig() {
    if (existsSync(CONFIG_FILE)) {
        rmSync(CONFIG_FILE, { force: true });
    }
    clearSharedConfigAuth();
}
export function getAuthToken() {
    return process.env.VIGTHORIA_TOKEN || loadAuthConfig().token;
}
async function requestJson(url, init) {
    const timeoutMsRaw = Number(process.env.VIGTHORIA_AUTH_TIMEOUT_MS || 8000);
    const timeoutMs = Number.isFinite(timeoutMsRaw) && timeoutMsRaw > 0 ? timeoutMsRaw : 8000;
    const controller = new AbortController();
    const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
    let response;
    try {
        response = await fetch(url, {
            ...init,
            signal: controller.signal,
            headers: {
                accept: 'application/json',
                'content-type': 'application/json',
                ...(init.headers || {}),
            },
        });
    }
    catch (error) {
        const name = error?.name;
        if (name === 'AbortError') {
            throw new Error(`Authentication request timed out after ${timeoutMs}ms`);
        }
        throw error;
    }
    finally {
        clearTimeout(timeoutHandle);
    }
    const text = await response.text();
    let body = {};
    if (text.trim()) {
        try {
            body = JSON.parse(text);
        }
        catch {
            if (/^\s*</.test(text) || /<!doctype html/i.test(text)) {
                throw new HttpError(response.status, 'The Vigthoria service returned an unexpected HTML response. Please verify auth routes.');
            }
            throw new HttpError(response.status, text.slice(0, 240));
        }
    }
    if (!response.ok) {
        const message = typeof body === 'object' && body && 'error' in body
            ? String(body.error)
            : typeof body === 'object' && body && 'message' in body
                ? String(body.message)
                : `Request failed with status ${response.status}`;
        throw new HttpError(response.status, message);
    }
    return body;
}
async function fetchV3ServiceKey(apiBase, token) {
    try {
        const endpoint = `${trimTrailingSlash(apiBase)}/api/cli/v3-service-key`;
        const result = await requestJson(endpoint, {
            method: 'GET',
            headers: { authorization: `Bearer ${token}` },
        });
        const key = result?.v3ServiceKey;
        if (typeof key === 'string' && key.trim()) {
            return key.trim();
        }
    }
    catch {
        // Non-fatal during login; V3 preflight can still fetch later via API utility.
    }
    return undefined;
}
async function ask(question, hidden = false) {
    const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
    if (!hidden) {
        try {
            return await new Promise((resolve) => rl.question(question, resolve));
        }
        finally {
            rl.close();
        }
    }
    const output = process.stdout;
    // Write the label directly so masking doesn't hide it
    output.write(question);
    const mutableRl = rl;
    const originalWrite = mutableRl._writeToOutput;
    mutableRl._writeToOutput = function writeMasked(value) {
        if (value.includes('\n') || value.includes('\r')) {
            output.write(value);
        }
        else if (value.length > 0) {
            output.write('*');
        }
    };
    try {
        return await new Promise((resolve) => rl.question('', resolve));
    }
    finally {
        if (originalWrite) {
            mutableRl._writeToOutput = originalWrite;
        }
        output.write('\n');
        rl.close();
    }
}
function markSuccessExit() {
    process.exitCode = 0;
}
function markErrorExit() {
    process.exitCode = 1;
}
export async function login(email, password) {
    try {
        const resolvedEmail = (email || '').trim();
        const resolvedPassword = password || '';
        if (!resolvedEmail || !resolvedPassword) {
            throw new Error('Email and password are required. Pass --email and --password or run login interactively.');
        }
        const authBases = getAuthBaseCandidates(getApiUrl());
        const endpointVariants = [
            { path: '/auth/login', body: { email: resolvedEmail, password: resolvedPassword } },
            { path: '/api/auth/login', body: { email: resolvedEmail, password: resolvedPassword } },
            { path: '/api/login-json', body: { identifier: resolvedEmail, password: resolvedPassword } },
            { path: '/api/login', body: { email: resolvedEmail, password: resolvedPassword } },
        ];
        const attempted = [];
        const failures = [];
        for (const base of authBases) {
            for (const variant of endpointVariants) {
                const endpoint = `${trimTrailingSlash(base)}${variant.path}`;
                attempted.push(endpoint);
                try {
                    const result = await requestJson(endpoint, {
                        method: 'POST',
                        body: JSON.stringify(variant.body),
                    });
                    const token = extractAuthToken(result);
                    if (!token) {
                        failures.push(`${endpoint} -> success without token`);
                        continue;
                    }
                    const normalizedBase = trimTrailingSlash(base);
                    const v3ServiceKey = await fetchV3ServiceKey(normalizedBase, token);
                    const config = {
                        apiUrl: normalizedBase,
                        token,
                        user: extractAuthUser(result, resolvedEmail),
                        ...(v3ServiceKey ? { v3ServiceKey } : {}),
                        success: true,
                    };
                    saveAuthConfig(config);
                    markSuccessExit();
                    return config;
                }
                catch (error) {
                    const message = humanMessage(error);
                    failures.push(`${endpoint} -> ${message}`);
                }
            }
        }
        throw new Error('Invalid email or password. Please check your credentials and try again.');
    }
    catch (error) {
        const message = humanMessage(error);
        markErrorExit();
        throw new Error(message);
    }
}
export async function logout() {
    const config = loadAuthConfig();
    if (config.token) {
        const bases = getAuthBaseCandidates(config.apiUrl || getApiUrl());
        const logoutPaths = ['/auth/logout', '/api/auth/logout', '/api/logout'];
        let remoteLogoutDone = false;
        for (const base of bases) {
            for (const route of logoutPaths) {
                try {
                    await requestJson(`${trimTrailingSlash(base)}${route}`, {
                        method: 'POST',
                        headers: { authorization: `Bearer ${config.token}` },
                    });
                    remoteLogoutDone = true;
                    break;
                }
                catch {
                    // Continue trying known routes.
                }
            }
            if (remoteLogoutDone) {
                break;
            }
        }
        if (!remoteLogoutDone) {
            console.warn(chalk.yellow('Remote logout endpoint not reachable; local credentials were still cleared.'));
        }
    }
    clearAuthConfig();
    process.exitCode = 0;
}
export async function whoami() {
    const config = loadAuthConfig();
    if (!config.token) {
        return undefined;
    }
    const bases = getAuthBaseCandidates(config.apiUrl || getApiUrl());
    const attempted = [];
    for (const base of bases) {
        const normalizedBase = trimTrailingSlash(base);
        const getRoutes = ['/auth/me', '/api/auth/me', '/api/user/info', '/api/profile'];
        for (const route of getRoutes) {
            const endpoint = `${normalizedBase}${route}`;
            attempted.push(endpoint);
            try {
                const result = await requestJson(endpoint, {
                    method: 'GET',
                    headers: { authorization: `Bearer ${config.token}` },
                });
                const user = extractAuthUser(result) || extractAuthUser(result.user);
                if (user) {
                    saveAuthConfig({ ...config, apiUrl: normalizedBase, user });
                    return user;
                }
            }
            catch {
                // continue fallback route probing
            }
        }
        const verifyEndpoint = `${normalizedBase}/api/auth/verify`;
        attempted.push(verifyEndpoint);
        try {
            const result = await requestJson(verifyEndpoint, {
                method: 'POST',
                headers: { authorization: `Bearer ${config.token}` },
                body: JSON.stringify({ token: config.token }),
            });
            const user = extractAuthUser(result) || extractAuthUser(result.user);
            if (user) {
                saveAuthConfig({ ...config, apiUrl: normalizedBase, user });
                return user;
            }
        }
        catch {
            // continue fallback route probing
        }
    }
    throw new Error(`Unable to verify account on known auth endpoints. Tried ${attempted.join(', ')}`);
}
export async function doctor() {
    const config = loadAuthConfig();
    const report = {
        nodeVersion: process.version,
        platform: process.platform,
        arch: process.arch,
        configFile: CONFIG_FILE,
        loggedIn: Boolean(config.token),
        apiUrl: config.apiUrl,
    };
    process.exitCode = 0;
    return report;
}
export async function handleLogin(options = {}) {
    try {
        if (options.token) {
            const config = {
                apiUrl: getApiUrl(),
                token: options.token,
                success: true,
            };
            const v3ServiceKey = await fetchV3ServiceKey(config.apiUrl, options.token);
            if (v3ServiceKey) {
                config.v3ServiceKey = v3ServiceKey;
            }
            saveAuthConfig(config);
            console.log(chalk.green('Logged in successfully with API token.'));
            process.exitCode = 0;
            return config;
        }
        let email = options.email?.trim();
        let password = options.password;
        if (options.device) {
            console.log(chalk.yellow('Device-code login is not enabled by this Vigthoria service. Falling back to email and password authentication.'));
        }
        if (!email) {
            email = (await ask('Email: ')).trim();
        }
        if (!password) {
            password = (await ask('Password: ', true)).trim();
        }
        if (!email || !password) {
            throw new Error('Email and password are required. Use --email and --password, or run vigthoria login interactively.');
        }
        const config = await login(email, password);
        console.log(chalk.green('Logged in successfully.'));
        if (config.user?.email) {
            console.log(`Account: ${config.user.email}`);
        }
        // Fetch and display subscription tier + role
        try {
            const subResponse = await fetch(`${config.apiUrl}/api/user/subscription`, {
                headers: { Authorization: `Bearer ${config.token}`, Cookie: `vigthoria-auth-token=${config.token}` },
                signal: AbortSignal.timeout(5000),
            });
            if (subResponse.ok) {
                const sub = await subResponse.json();
                const subRecord = (sub.subscription && typeof sub.subscription === 'object') ? sub.subscription : {};
                const userRecord = (sub.user && typeof sub.user === 'object') ? sub.user : {};
                const plan = String(sub.plan || subRecord.plan || userRecord.plan || userRecord.subscription_plan || '').trim();
                const role = String(sub.role || subRecord.role || userRecord.role || userRecord.user_role || '').trim();
                if (plan) {
                    console.log(`Plan:  ${plan.toUpperCase()}`);
                }
                if (role) {
                    console.log(`Role:  ${role}`);
                }
            }
        }
        catch { /* non-fatal — login already succeeded */ }
        try {
            const sharedConfig = new Config();
            await sharedConfig.refreshHubModelPreferences();
            const hubPrefs = sharedConfig.get('hubModelPrefs');
            if (hubPrefs?.balance !== undefined) {
                console.log(`Cloud credits: ${hubPrefs.balance}`);
            }
        }
        catch { /* non-fatal */ }
        console.log(`API: ${config.apiUrl}`);
        process.exitCode = 0;
        return config;
    }
    catch (error) {
        console.error(chalk.red(`Login failed: ${humanMessage(error)}`));
        process.exitCode = 1;
        return undefined;
    }
}
export async function handleLogout(_options) {
    try {
        await logout();
        console.log(chalk.green('Logged out successfully.'));
        process.exitCode = 0;
    }
    catch (error) {
        console.error(chalk.red(`Logout failed: ${humanMessage(error)}`));
        process.exitCode = 1;
    }
}
export async function statusAction() {
    try {
        const legacyConfig = loadAuthConfig();
        const sharedConfig = new Config();
        const sharedApiUrl = String(sharedConfig.get('apiUrl') || '').trim();
        const sharedToken = String(sharedConfig.get('authToken') || '').trim();
        const effectiveApiUrl = trimTrailingSlash(sharedApiUrl || legacyConfig.apiUrl || getApiUrl());
        const effectiveToken = sharedToken || legacyConfig.token || '';
        if (!effectiveToken) {
            console.log(chalk.yellow('Not logged in.'));
            process.exitCode = 0;
            return;
        }
        const authProbeHeaders = {
            Authorization: `Bearer ${effectiveToken}`,
            Cookie: `vigthoria-auth-token=${effectiveToken}`,
        };
        let authLooksValid = true;
        try {
            const authProbe = await fetch(`${effectiveApiUrl}/api/user/subscription`, { headers: authProbeHeaders, signal: AbortSignal.timeout(6000) });
            if (authProbe.status === 401 || authProbe.status === 403) {
                authLooksValid = false;
            }
        }
        catch {
            // Network failures should not force logout status.
        }
        if (!authLooksValid) {
            console.log(chalk.yellow('Not logged in. Session expired or invalid.'));
            console.log(chalk.gray('Run: vigthoria login'));
            process.exitCode = 1;
            return;
        }
        let user = legacyConfig.user;
        try {
            user = await whoami();
        }
        catch {
            // Keep local status available even if remote whoami endpoint is down.
        }
        let overallStatus = 'Unknown';
        let coderStatus = 'Unknown';
        let modelsStatus = 'Unknown';
        const modelsApiBase = trimTrailingSlash(String(sharedConfig.get('modelsApiUrl') || 'https://api.vigthoria.io'));
        try {
            const [coderResponse, modelsResponse] = await Promise.all([
                fetch(`${effectiveApiUrl}/api/health`, { signal: AbortSignal.timeout(8000) }),
                fetch(`${modelsApiBase}/health`, { signal: AbortSignal.timeout(8000) }),
            ]);
            const coderJson = await coderResponse.json().catch(() => ({}));
            const modelsJson = await modelsResponse.json().catch(() => ({}));
            const coderOk = coderResponse.ok && (coderJson.status === 'ok' || coderJson.healthy === true);
            const modelsOk = modelsResponse.ok && (modelsJson.status === 'ok' || modelsJson.status === 'healthy' || modelsJson.healthy === true);
            coderStatus = coderOk ? 'Online' : 'Offline';
            modelsStatus = modelsOk ? 'Online' : 'Offline';
            overallStatus = coderOk && modelsOk ? 'Healthy' : 'Degraded';
        }
        catch {
            // Status should still render even if health probes fail.
        }
        const plan = String(sharedConfig.get('subscription')?.plan || '').trim();
        console.log(chalk.white('Account Status'));
        console.log(chalk.green('Logged in.'));
        if (user?.email) {
            console.log(`Account: ${user.email}`);
        }
        if (plan) {
            console.log(`Plan: ${plan.toUpperCase()}`);
        }
        console.log(`Overall: ${overallStatus}`);
        console.log(`Coder API: ${coderStatus}`);
        console.log(`Models API: ${modelsStatus}`);
        console.log(`API: ${effectiveApiUrl}`);
        process.exitCode = 0;
    }
    catch (error) {
        console.error(chalk.red(`Unable to read status: ${humanMessage(error)}`));
        process.exitCode = 1;
    }
}
export function registerAuthCommands(program) {
    const auth = program.command('auth').description('Manage Vigthoria CLI authentication');
    auth
        .command('login')
        .description('Sign in with your Vigthoria account')
        .option('-t, --token <token>', 'API token for token-based authentication')
        .option('-e, --email <email>', 'Account email address')
        .option('-p, --password <password>', 'Account password')
        .option('--device', 'Use OAuth device flow (requires server support)')
        .action(async (options) => {
        await handleLogin(options);
    });
    auth
        .command('logout')
        .description('Sign out and remove saved credentials')
        .action(async () => {
        await handleLogout();
    });
    auth
        .command('whoami')
        .description('Show the authenticated Vigthoria account')
        .action(async () => {
        try {
            const user = await whoami();
            if (!user) {
                console.log(chalk.yellow('Not logged in.'));
                process.exitCode = 0;
                return;
            }
            console.log(chalk.green('Logged in.'));
            console.log(user.email || user.name || user.id || 'Authenticated user');
            process.exitCode = 0;
        }
        catch (error) {
            console.error(chalk.red(`Unable to fetch account: ${humanMessage(error)}`));
            process.exitCode = 1;
        }
    });
}
