"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.init = init;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const promises_1 = __importDefault(require("node:readline/promises"));
const node_process_1 = require("node:process");
const node_child_process_1 = require("node:child_process");
const types_1 = require("@playdrop/types");
const apiClient_1 = require("../apiClient");
const commandContext_1 = require("../commandContext");
const http_1 = require("../http");
const messages_1 = require("../messages");
const clientInfo_1 = require("../clientInfo");
const DEFAULT_CATALOGUE = '{}\n';
const LEGACY_CATALOGUE_VERSION_KEY = ['schema', 'Version'].join('');
const ALLOWED_CATALOGUE_TOP_LEVEL_KEYS = new Set(['apps', 'assets', 'assetPacks']);
function ensureTrailingNewline(content) {
    return content.endsWith('\n') ? content : `${content}\n`;
}
function renderReadmeTemplate(template, replacements) {
    let rendered = template;
    for (const [key, value] of Object.entries(replacements)) {
        const token = `{${key}}`;
        rendered = rendered.split(token).join(value);
    }
    return rendered;
}
async function resolveUsername(apiBase, token) {
    const client = (0, apiClient_1.createCliApiClient)({ baseUrl: apiBase, token });
    try {
        const data = await client.me();
        const username = data.user?.username?.trim();
        if (!username) {
            (0, messages_1.printErrorWithHelp)('The Playdrop API returned an unexpected response.', [
                'Retry "playdrop project init" in a moment.',
                'If the problem continues, contact the Playdrop team.'
            ], { command: 'project init' });
            process.exitCode = 1;
            return null;
        }
        return username;
    }
    catch (unknownError) {
        if (unknownError instanceof types_1.UnsupportedClientError) {
            (0, http_1.handleUnsupportedError)(unknownError, 'Project bootstrap');
            return null;
        }
        if (unknownError instanceof http_1.CLIUnsupportedClientError) {
            throw unknownError;
        }
        if (unknownError instanceof types_1.ApiError) {
            (0, messages_1.printErrorWithHelp)(`Request failed with status ${unknownError.status}.`, [
                'Run "playdrop auth login" to refresh your credentials.',
                'Use "playdrop project init" again after logging in.'
            ], { command: 'project init' });
            process.exitCode = 1;
            return null;
        }
        const message = unknownError instanceof Error ? unknownError.message : String(unknownError);
        (0, messages_1.printErrorWithHelp)('Could not reach the Playdrop API to resolve your account information.', [message], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
}
async function promptGitInitialization(label) {
    if (!node_process_1.stdin.isTTY || !node_process_1.stdout.isTTY) {
        return false;
    }
    const rl = promises_1.default.createInterface({ input: node_process_1.stdin, output: node_process_1.stdout });
    const answer = await rl.question(`No git repository detected in ${label}. Run "git init"? (y/N) `);
    rl.close();
    return /^y(es)?$/i.test(answer.trim());
}
function runGitInit(directory) {
    const result = (0, node_child_process_1.spawnSync)('git', ['init'], {
        cwd: directory,
        stdio: 'inherit',
    });
    return result.status === 0;
}
function normalizeBootstrapPayload(payload) {
    if (!payload) {
        return null;
    }
    const readmeUri = typeof payload.readmeUri === 'string' ? payload.readmeUri.trim() : '';
    if (!readmeUri) {
        (0, messages_1.printErrorWithHelp)('Bootstrap payload is missing a README URI.', ['Contact the platform team or try again shortly.'], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    const agentsUri = typeof payload.agentsUri === 'string' ? payload.agentsUri.trim() : '';
    if (!agentsUri) {
        (0, messages_1.printErrorWithHelp)('Bootstrap payload is missing an AGENTS URI.', ['Contact the platform team or try again shortly.'], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    const catalogueRaw = typeof payload.catalogue === 'string' && payload.catalogue.trim().length > 0
        ? payload.catalogue
        : DEFAULT_CATALOGUE;
    const catalogueCandidate = ensureTrailingNewline(catalogueRaw.trim() ? catalogueRaw : DEFAULT_CATALOGUE);
    let parsedCatalogue;
    try {
        parsedCatalogue = JSON.parse(catalogueCandidate);
    }
    catch {
        (0, messages_1.printErrorWithHelp)('Bootstrap payload returned an invalid catalogue.json.', ['Contact the platform team or try again shortly.'], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    if (!parsedCatalogue || typeof parsedCatalogue !== 'object' || Array.isArray(parsedCatalogue)) {
        (0, messages_1.printErrorWithHelp)('Bootstrap payload returned an invalid catalogue.json.', ['Contact the platform team or try again shortly.'], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    if (Object.prototype.hasOwnProperty.call(parsedCatalogue, LEGACY_CATALOGUE_VERSION_KEY)) {
        (0, messages_1.printErrorWithHelp)('Bootstrap payload returned a legacy catalogue.json with a top-level version field.', ['Contact the platform team or try again shortly.'], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    const unsupportedKey = Object.keys(parsedCatalogue).find((key) => !ALLOWED_CATALOGUE_TOP_LEVEL_KEYS.has(key));
    if (unsupportedKey) {
        (0, messages_1.printErrorWithHelp)(`Bootstrap payload returned catalogue.json with unsupported top-level key "${unsupportedKey}".`, ['Contact the platform team or try again shortly.'], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    const catalogue = catalogueCandidate;
    return { readmeUri, agentsUri, catalogue };
}
async function downloadBootstrapAsset(webBase, uri, label) {
    if (!webBase) {
        (0, messages_1.printErrorWithHelp)(`Cannot download ${label} because the current environment is missing a web base URL.`, [
            'Set PLAYDROP_WEB_BASE, PLAYDROP_WEB_BASE_DEV, or the appropriate environment override before running "playdrop project init".'
        ], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    const url = new URL(uri, webBase).toString();
    let response;
    try {
        response = await fetch(url);
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        (0, messages_1.printErrorWithHelp)(`Failed to download ${label} from ${url}.`, [message], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    if (!response.ok) {
        (0, messages_1.printErrorWithHelp)(`Failed to download ${label} from ${url}.`, [`HTTP ${response.status} ${response.statusText}`], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    const text = await response.text();
    return ensureTrailingNewline(text);
}
async function fetchBootstrap(base, token) {
    const client = (0, apiClient_1.createCliApiClient)({ baseUrl: base, token });
    try {
        const payload = await client.fetchBootstrap();
        return payload;
    }
    catch (unknownError) {
        if (unknownError instanceof types_1.UnsupportedClientError) {
            (0, http_1.handleUnsupportedError)(unknownError, 'Project bootstrap');
            return null;
        }
        if (unknownError instanceof http_1.CLIUnsupportedClientError) {
            throw unknownError;
        }
        if (unknownError instanceof types_1.ApiError) {
            throw unknownError;
        }
        throw unknownError;
    }
}
async function init(targetPath, options = {}) {
    const resolved = (0, node_path_1.resolve)(targetPath || '.');
    let stats = null;
    try {
        stats = (0, node_fs_1.statSync)(resolved);
    }
    catch {
        // directory will be created
    }
    if (stats && !stats.isDirectory()) {
        (0, messages_1.printErrorWithHelp)(`Cannot initialize a Playdrop project because ${resolved} is not a directory.`, [
            'Choose a directory path (use "." for the current folder) or create the directory before running "playdrop project init".'
        ], { command: 'project init' });
        process.exitCode = 1;
        return null;
    }
    (0, node_fs_1.mkdirSync)(resolved, { recursive: true });
    const cataloguePath = (0, node_path_1.join)(resolved, 'catalogue.json');
    const catalogueExisted = (0, node_fs_1.existsSync)(cataloguePath);
    const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project init', 'Bootstrapping a Playdrop project', { workspacePath: resolved });
    if (!ctx) {
        return null;
    }
    const { envConfig, token } = ctx;
    let bootstrap = null;
    try {
        bootstrap = await fetchBootstrap(envConfig.apiBase, token);
    }
    catch (error) {
        if (error instanceof http_1.CLIUnsupportedClientError) {
            return null;
        }
        throw error;
    }
    const normalized = normalizeBootstrapPayload(bootstrap);
    if (!normalized) {
        return null;
    }
    const readmePath = (0, node_path_1.join)(resolved, 'README.md');
    const agentsPath = (0, node_path_1.join)(resolved, 'AGENTS.md');
    const actions = [];
    const readmeExisted = (0, node_fs_1.existsSync)(readmePath);
    const agentsExisted = (0, node_fs_1.existsSync)(agentsPath);
    let username = null;
    try {
        username = await resolveUsername(envConfig.apiBase, token);
    }
    catch (error) {
        if (error instanceof http_1.CLIUnsupportedClientError) {
            return null;
        }
        throw error;
    }
    if (!username) {
        return null;
    }
    const readmeDownload = await downloadBootstrapAsset(envConfig.webBase, normalized.readmeUri, 'README.md');
    if (!readmeDownload) {
        return null;
    }
    const replacements = {
        username,
        version: (0, clientInfo_1.getCliVersion)(),
        build: String((0, clientInfo_1.getCliBuild)()),
        date: new Date().toISOString().slice(0, 10),
    };
    const renderedReadme = ensureTrailingNewline(renderReadmeTemplate(readmeDownload, replacements));
    (0, node_fs_1.writeFileSync)(readmePath, renderedReadme, 'utf8');
    actions.push(readmeExisted ? 'Updated README.md' : 'Created README.md');
    const agentsDownload = await downloadBootstrapAsset(envConfig.webBase, normalized.agentsUri, 'AGENTS.md');
    if (!agentsDownload) {
        return null;
    }
    (0, node_fs_1.writeFileSync)(agentsPath, agentsDownload, 'utf8');
    actions.push(agentsExisted ? 'Updated AGENTS.md' : 'Created AGENTS.md');
    if (!catalogueExisted) {
        (0, node_fs_1.writeFileSync)(cataloguePath, normalized.catalogue, 'utf8');
        actions.push('Created catalogue.json');
    }
    else {
        actions.push('Skipped existing catalogue.json');
    }
    const label = (0, node_path_1.relative)(process.cwd(), resolved) || '.';
    const gitDirPath = (0, node_path_1.join)(resolved, '.git');
    const hasGitRepo = (0, node_fs_1.existsSync)(gitDirPath);
    let gitStatus = 'skipped';
    if (!hasGitRepo) {
        if (!options.quiet && node_process_1.stdin.isTTY && node_process_1.stdout.isTTY) {
            const consent = await promptGitInitialization(label);
            if (consent) {
                const success = runGitInit(resolved);
                if (success) {
                    gitStatus = 'initialized';
                    actions.push('Initialized git repository');
                }
                else {
                    gitStatus = 'declined';
                    actions.push('git init failed (install Git and retry)');
                    process.exitCode = process.exitCode || 1;
                }
            }
            else {
                gitStatus = 'declined';
                actions.push('Skipped git init (declined)');
            }
        }
        else if (!options.quiet) {
            actions.push('Skipped git init (non-interactive)');
        }
    }
    else if (!options.quiet) {
        actions.push('Detected existing git repository');
    }
    const summary = {
        directory: resolved,
        cataloguePath,
        readmeStatus: readmeExisted ? 'updated' : 'created',
        agentsStatus: agentsExisted ? 'updated' : 'created',
        catalogueStatus: catalogueExisted ? 'skipped' : 'created',
        gitStatus,
    };
    if (!options.quiet) {
        console.log(`Initialized Playdrop project in ${label}`);
        for (const action of actions) {
            console.log(` - ${action}`);
        }
        console.log('\nNext steps:');
        console.log(' - Run "playdrop project create app <app-name>" to scaffold your first app.');
        console.log(' - Use "playdrop project dev <app-name>" to preview locally.');
        console.log(' - Publish with "playdrop project publish ." when ready to share.');
    }
    return summary;
}
