"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowserLoginService = void 0;
exports.openBrowser = openBrowser;
exports.validateJwt = validateJwt;
const http_1 = __importDefault(require("../utils/http"));
const child_process_1 = require("child_process");
const util_1 = require("util");
const os = __importStar(require("os"));
const fs = __importStar(require("fs"));
const KnowhowClient_1 = require("../services/KnowhowClient");
const spinner_1 = require("./spinner");
const errors_1 = require("./errors");
class BrowserLoginService {
    orgId;
    baseUrl;
    constructor(baseUrl = KnowhowClient_1.KNOWHOW_API_URL, orgId) {
        this.orgId = orgId;
        if (!baseUrl) {
            throw new errors_1.BrowserLoginError("KNOWHOW_API_URL environment variable not set");
        }
        this.baseUrl = baseUrl;
        this.setupSignalHandlers();
    }
    async login() {
        const spinner = new spinner_1.Spinner();
        let isAborted = false;
        try {
            spinner.start("Creating login session");
            const sessionData = await this.createSession();
            spinner.stop();
            spinner.start("Opening browser for authentication");
            let browserUrl = sessionData.browserUrl;
            if (this.orgId) {
                const separator = browserUrl.includes("?") ? "&" : "?";
                browserUrl = `${browserUrl}${separator}orgId=${encodeURIComponent(this.orgId)}`;
            }
            await openBrowser(browserUrl);
            console.log(`\nIf the browser didn't open automatically, please visit: ${browserUrl}\n`);
            spinner.stop();
            spinner.start("Waiting for browser authentication");
            const abortHandler = () => {
                isAborted = true;
                spinner.stop();
            };
            process.once("SIGINT", abortHandler);
            let attempt = 0;
            const maxAttempts = 60;
            while (attempt < maxAttempts) {
                attempt++;
                try {
                    if (isAborted) {
                        throw new errors_1.BrowserLoginError("Authentication cancelled by user", "USER_CANCELLED");
                    }
                    const statusResponse = await http_1.default.get(`${this.baseUrl}/api/cli-login/session/${sessionData.sessionId}/status`, { timeout: 10000 });
                    const status = statusResponse.data;
                    if (status.status.toLowerCase() === "approved") {
                        spinner.stop();
                        spinner.start("Authentication successful! Retrieving token");
                        const tokenResponse = await http_1.default.post(`${this.baseUrl}/api/cli-login/session/${sessionData.sessionId}/token`);
                        const jwt = tokenResponse.data.jwt;
                        await this.storeJwt(jwt);
                        spinner.stop();
                        return;
                    }
                    else if (status.status.toLowerCase() === "denied") {
                        throw new errors_1.BrowserLoginError("Authentication was denied", "AUTH_DENIED");
                    }
                    else if (status.status.toLowerCase() === "expired") {
                        throw new errors_1.BrowserLoginError("Authentication session expired", "SESSION_EXPIRED");
                    }
                }
                catch (error) {
                    if (http_1.default.isHttpError(error) && error.status !== 408) {
                        throw new errors_1.BrowserLoginError(`Network error: ${error.message}`, "NETWORK_ERROR");
                    }
                }
                await this.sleep(5000);
            }
            process.removeListener("SIGINT", abortHandler);
            throw new errors_1.BrowserLoginError("Authentication timed out", "TIMEOUT");
        }
        catch (error) {
            spinner.stop();
            throw error;
        }
    }
    async createSession() {
        try {
            const response = await http_1.default.post(`${this.baseUrl}/api/cli-login/session`, {});
            return response.data;
        }
        catch (error) {
            if (http_1.default.isHttpError(error)) {
                throw new errors_1.BrowserLoginError(`Failed to create login session: ${error.message}`, "SESSION_CREATE_FAILED");
            }
            throw new errors_1.BrowserLoginError(`Unexpected error creating session: ${error.message}`);
        }
    }
    async storeJwt(jwt) {
        const configDir = `${process.cwd()}/.knowhow`;
        const jwtFile = `${configDir}/.jwt`;
        if (!fs.existsSync(configDir)) {
            fs.mkdirSync(configDir, { recursive: true });
        }
        fs.writeFileSync(jwtFile, jwt, { mode: 0o600 });
        fs.chmodSync(jwtFile, 0o600);
    }
    async sleep(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
    }
    setupSignalHandlers() {
        const gracefulShutdown = () => {
            console.log("\n\nAuthentication cancelled by user.");
            process.exit(1);
        };
        process.on("SIGTERM", gracefulShutdown);
    }
}
exports.BrowserLoginService = BrowserLoginService;
async function openBrowser(url) {
    const execAsync = (0, util_1.promisify)(child_process_1.exec);
    try {
        const platform = os.platform();
        console.log(`Opening browser for URL: ${url} on platform: ${platform}`);
        let command;
        switch (platform) {
            case "darwin":
                command = `open "${url}"`;
                break;
            case "win32":
                command = `start "" "${url}"`;
                break;
            default:
                command = `xdg-open "${url}"`;
                break;
        }
        await execAsync(command);
    }
    catch (error) {
        console.warn(`Could not automatically open browser: ${error.message}`);
    }
}
function validateJwt(jwt) {
    if (!jwt || typeof jwt !== "string") {
        return false;
    }
    const parts = jwt.split(".");
    if (parts.length !== 3) {
        return false;
    }
    for (const part of parts) {
        if (!part || part.trim().length === 0) {
            return false;
        }
    }
    const looksLikeRealJwt = parts.every(part => /^[A-Za-z0-9+/\-_]+={0,2}$/.test(part));
    if (looksLikeRealJwt) {
        try {
            JSON.parse(Buffer.from(parts[0], "base64").toString());
            JSON.parse(Buffer.from(parts[1], "base64").toString());
            const signature = Buffer.from(parts[2], "base64");
            if (signature.length < 32) {
                return false;
            }
        }
        catch (error) {
            return false;
        }
    }
    try {
        return true;
    }
    catch {
        return true;
    }
}
//# sourceMappingURL=browserLogin.js.map