"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDevError = createDevError;
exports.isNetworkError = isNetworkError;
exports.fetchDevUser = fetchDevUser;
exports.fetchDevUsername = fetchDevUsername;
exports.assertAppRegistered = assertAppRegistered;
exports.resolveDevTarget = resolveDevTarget;
exports.findProjectInfo = findProjectInfo;
exports.formatProjectLabel = formatProjectLabel;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const catalogue_utils_1 = require("../catalogue-utils");
function cloneSurfaceTargets(targets) {
    return {
        list: [...targets.list],
        map: { ...targets.map },
    };
}
const DEFAULT_SURFACE_TARGETS = (0, catalogue_utils_1.parseSurfaceTargets)({ desktop: true, mobileLandscape: false, mobilePortrait: false });
function createDevError(code, message) {
    const error = new Error(message);
    error.code = code;
    return error;
}
function isNetworkError(error) {
    if (!error || typeof error !== 'object') {
        return false;
    }
    const candidate = error;
    if (candidate instanceof TypeError) {
        return true;
    }
    const code = typeof candidate.code === 'string' ? candidate.code : undefined;
    if (code === 'ECONNREFUSED' || code === 'ENOTFOUND') {
        return true;
    }
    const cause = candidate.cause;
    if (cause && typeof cause === 'object' && typeof cause.code === 'string') {
        const nestedCode = cause.code;
        return nestedCode === 'ECONNREFUSED' || nestedCode === 'ENOTFOUND';
    }
    return false;
}
async function fetchDevUser(client) {
    const response = await client.me();
    const user = response?.user;
    if (user && typeof user.username === 'string' && user.username.trim().length > 0) {
        return { ...user, username: user.username.trim() };
    }
    throw new Error('DEV_USERNAME_EMPTY');
}
async function fetchDevUsername(client) {
    const user = await fetchDevUser(client);
    return user.username;
}
async function assertAppRegistered(client, creatorUsername, appName) {
    const response = await client.fetchAppBySlug(creatorUsername, appName);
    if (!response?.app) {
        throw new Error('app_not_found');
    }
    return response.app;
}
function findNearestCatalogue(startDir) {
    let current = (0, node_path_1.resolve)(startDir);
    while (true) {
        const candidate = (0, node_path_1.join)(current, 'catalogue.json');
        if ((0, node_fs_1.existsSync)(candidate)) {
            return candidate;
        }
        const parent = (0, node_path_1.dirname)(current);
        if (parent === current) {
            return null;
        }
        current = parent;
    }
}
function parseCatalogueApps(cataloguePath) {
    try {
        const raw = (0, node_fs_1.readFileSync)(cataloguePath, 'utf8');
        const data = raw.trim() ? JSON.parse(raw) : {};
        if (Array.isArray(data)) {
            return data;
        }
        if (data && typeof data === 'object' && Array.isArray(data.apps)) {
            return data.apps;
        }
        return [];
    }
    catch (error) {
        throw createDevError('invalid_catalogue', `Failed to parse catalogue at ${cataloguePath}: ${error?.message || 'invalid JSON'}`);
    }
}
function resolveAppFromCatalogue(cataloguePath, appName) {
    const apps = parseCatalogueApps(cataloguePath).filter(entry => entry && typeof entry.name === 'string');
    if (apps.length === 0) {
        throw createDevError('empty_catalogue', `No apps found in ${cataloguePath}.`);
    }
    let selected;
    if (appName) {
        selected = apps.find(entry => entry.name.trim() === appName.trim());
        if (!selected) {
            throw createDevError('app_not_found', `App "${appName}" was not found in ${cataloguePath}.`);
        }
    }
    else if (apps.length === 1) {
        selected = apps[0];
    }
    else {
        throw createDevError('app_required', `Multiple apps found in ${cataloguePath}. Use --app <name> to choose one.`);
    }
    const entryName = selected.name.trim();
    const relativeFile = typeof selected.file === 'string' && selected.file.trim().length > 0
        ? selected.file.trim()
        : `${entryName}.html`;
    const htmlPath = (0, node_path_1.resolve)((0, node_path_1.dirname)(cataloguePath), relativeFile);
    const info = (0, catalogue_utils_1.findAppCatalogueInfo)(htmlPath);
    return {
        appName: info.name,
        htmlPath,
        cataloguePath: info.cataloguePath,
        surfaceTargets: cloneSurfaceTargets(info.surfaceTargets),
        ecsPath: info.ecsPath,
        serverScriptPath: info.serverScriptPath,
    };
}
function resolveDevTarget(targetArg, appOption) {
    if (!targetArg) {
        const cataloguePath = findNearestCatalogue(process.cwd());
        if (!cataloguePath) {
            throw createDevError('catalogue_missing', 'Run "playdrop project dev" inside a workspace or provide a path to an app HTML file.');
        }
        return resolveAppFromCatalogue(cataloguePath, appOption);
    }
    const absoluteTarget = (0, node_path_1.resolve)(targetArg);
    if ((0, node_fs_1.existsSync)(absoluteTarget)) {
        const stats = (0, node_fs_1.statSync)(absoluteTarget);
        if (stats.isDirectory()) {
            const cataloguePath = (0, node_path_1.join)(absoluteTarget, 'catalogue.json');
            if (!(0, node_fs_1.existsSync)(cataloguePath)) {
                throw createDevError('catalogue_missing', `Directory ${absoluteTarget} does not contain catalogue.json.`);
            }
            return resolveAppFromCatalogue(cataloguePath, appOption);
        }
        if (!stats.isFile()) {
            throw createDevError('invalid_target', `Target ${absoluteTarget} is not a file.`);
        }
        if (!absoluteTarget.toLowerCase().endsWith('.html')) {
            throw createDevError('invalid_target', 'Provide an HTML file when using a direct file path.');
        }
        let surfaceTargets = cloneSurfaceTargets(DEFAULT_SURFACE_TARGETS);
        let derivedName = (0, node_path_1.basename)(absoluteTarget, '.html');
        let derivedCataloguePath = null;
        try {
            const info = (0, catalogue_utils_1.findAppCatalogueInfo)(absoluteTarget);
            surfaceTargets = cloneSurfaceTargets(info.surfaceTargets);
            derivedName = info.name;
            derivedCataloguePath = info.cataloguePath;
            return {
                appName: info.name,
                htmlPath: absoluteTarget,
                cataloguePath: info.cataloguePath,
                surfaceTargets,
                ecsPath: info.ecsPath,
                serverScriptPath: info.serverScriptPath,
            };
        }
        catch {
            // fall back to default surface targets
        }
        return {
            appName: derivedName,
            htmlPath: absoluteTarget,
            cataloguePath: derivedCataloguePath,
            surfaceTargets,
            ecsPath: null,
            serverScriptPath: null,
        };
    }
    const cataloguePath = findNearestCatalogue(process.cwd());
    if (!cataloguePath) {
        throw createDevError('catalogue_missing', `Could not find catalogue.json for app "${targetArg}". Run "playdrop project dev" from a workspace or provide a file path.`);
    }
    return resolveAppFromCatalogue(cataloguePath, targetArg);
}
function findProjectInfo(htmlPath) {
    let current = (0, node_path_1.dirname)(htmlPath);
    const visited = new Set();
    while (!visited.has(current)) {
        visited.add(current);
        const candidate = (0, node_path_1.join)(current, 'package.json');
        if ((0, node_fs_1.existsSync)(candidate) && (0, node_fs_1.statSync)(candidate).isFile()) {
            try {
                const pkg = JSON.parse((0, node_fs_1.readFileSync)(candidate, 'utf8'));
                return { projectDir: current, packageJsonPath: candidate, packageJson: pkg };
            }
            catch {
                return { projectDir: current, packageJsonPath: candidate };
            }
        }
        const parent = (0, node_path_1.dirname)(current);
        if (parent === current) {
            break;
        }
        current = parent;
    }
    return { projectDir: null, packageJsonPath: null };
}
function formatProjectLabel(projectInfo) {
    if (!projectInfo.projectDir)
        return null;
    return (0, node_path_1.relative)(process.cwd(), projectInfo.projectDir) || projectInfo.projectDir;
}
