'use strict';

const CATEGORY_ORDER = ['generation', 'management', 'introspection', 'utility'];
const CATEGORY_LABEL = {
    generation: 'Generation',
    management: 'Management',
    introspection: 'Introspection',
    utility: 'Utility'
};
const GLOBAL_OPTION_LABELS = ['--help, -h', '--version, -v'];

function calculateColumnWidth(labels) {
    if (!Array.isArray(labels) || labels.length === 0) return 0;
    let max = 0;
    for (const l of labels) {
        const len = typeof l === 'string' ? l.length : String(l).length;
        if (len > max) max = len;
    }
    return max + 2;
}

function padRight(str, width) {
    if (str.length >= width) return str + ' ';
    return str + ' '.repeat(width - str.length);
}

function commandLabel(contract) {
    if (contract.resource) {
        return `${contract.resource} ${contract.verb}`;
    }
    return contract.verb;
}

function shortDescription(desc) {
    if (!desc) return '';
    const trimmed = desc.trim();
    if (trimmed.endsWith('.')) return trimmed.slice(0, -1);
    return trimmed;
}

function collectContracts(manifest, loadCommand) {
    const items = [];

    if (manifest && manifest.globalVerbs) {
        for (const verb of Object.keys(manifest.globalVerbs).sort()) {
            const filePath = manifest.globalVerbs[verb];
            try {
                const contract = loadCommand(filePath);
                items.push({ contract, sortKey: verb });
            } catch (_err) {
                // skip invalid contract (still allow help to render others)
            }
        }
    }

    if (manifest && manifest.resources) {
        for (const resource of Object.keys(manifest.resources).sort()) {
            const verbs = manifest.resources[resource];
            for (const verb of Object.keys(verbs).sort()) {
                const filePath = verbs[verb];
                try {
                    const contract = loadCommand(filePath);
                    items.push({ contract, sortKey: `${resource} ${verb}` });
                } catch (_err) {
                    // skip invalid contract
                }
            }
        }
    }

    return items;
}

function generateGeneralHelp(manifest, loadCommand) {
    const lines = [];
    lines.push('RESTForge CLI Generator');
    lines.push('');
    lines.push('Usage:');
    lines.push('  npx restforge <command> [options]');
    lines.push('  npx restforge <resource> <verb> [options]');
    lines.push('');

    const items = collectContracts(manifest, loadCommand);

    const commandLabels = items.map((item) => commandLabel(item.contract));
    const colWidth = calculateColumnWidth(commandLabels.concat(GLOBAL_OPTION_LABELS));

    const byCategory = {};
    for (const item of items) {
        const cat = item.contract.category;
        if (!byCategory[cat]) byCategory[cat] = [];
        byCategory[cat].push(item);
    }

    const allCategories = new Set([...CATEGORY_ORDER, ...Object.keys(byCategory)]);

    for (const cat of CATEGORY_ORDER.concat(
        Array.from(allCategories).filter((c) => !CATEGORY_ORDER.includes(c)).sort()
    )) {
        const entries = byCategory[cat];
        if (!entries || entries.length === 0) continue;

        const label = CATEGORY_LABEL[cat] || cat.charAt(0).toUpperCase() + cat.slice(1);
        lines.push(`${label}:`);

        entries.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
        for (const item of entries) {
            const name = commandLabel(item.contract);
            const desc = shortDescription(item.contract.description);
            lines.push(`  ${padRight(name, colWidth)} ${desc}`);
        }
        lines.push('');
    }

    if (items.length === 0) {
        lines.push('(no commands available)');
        lines.push('');
    }

    lines.push('Global Options:');
    lines.push(`  ${padRight('--help, -h', colWidth)} Tampilkan help message`);
    lines.push(`  ${padRight('--version, -v', colWidth)} Tampilkan versi package`);
    lines.push('');
    lines.push('Untuk help spesifik command: npx restforge <command> --help');

    return lines.join('\n');
}

function flagSignature(flagDef) {
    if (flagDef.type === 'boolean') return '';
    return ` <${flagDef.type}>`;
}

function defaultRepr(def) {
    if (def === null) return 'null';
    if (def === undefined) return 'undefined';
    if (typeof def === 'string') return def === '' ? '(empty)' : def;
    return String(def);
}

function generateCommandHelp(contract) {
    if (!contract || typeof contract !== 'object') {
        throw new Error('generateCommandHelp: contract must be an object');
    }

    const lines = [];
    const name = commandLabel(contract);
    lines.push(`Command: ${name}`);
    lines.push('');
    lines.push(contract.description);
    lines.push('');

    const flags = contract.flags || {};
    const positional = Array.isArray(contract.positional) ? contract.positional : [];

    const usageParts = ['npx restforge'];
    if (contract.resource) usageParts.push(contract.resource);
    usageParts.push(contract.verb);

    for (const posDef of positional) {
        const token = posDef.required ? `<${posDef.name}>` : `[${posDef.name}]`;
        usageParts.push(token);
    }

    const requiredFlagNames = [];
    const optionalFlagNames = [];
    for (const [flagName, flagDef] of Object.entries(flags)) {
        if (flagDef.required) requiredFlagNames.push(flagName);
        else optionalFlagNames.push(flagName);
    }

    for (const fname of requiredFlagNames) {
        const fdef = flags[fname];
        if (fdef.type === 'boolean') {
            usageParts.push(`--${fname}`);
        } else {
            usageParts.push(`--${fname}=<${fdef.type.toUpperCase()}>`);
        }
    }
    if (optionalFlagNames.length > 0) usageParts.push('[options]');

    lines.push('Usage:');
    lines.push(`  ${usageParts.join(' ')}`);
    lines.push('');

    const labels = [];
    for (const posDef of positional) {
        const sig = posDef.required ? `<${posDef.name}>` : `[${posDef.name}]`;
        labels.push(`${sig} <${posDef.type}>`);
    }
    for (const fname of requiredFlagNames) {
        labels.push(`--${fname}${flagSignature(flags[fname])}`);
    }
    for (const fname of optionalFlagNames) {
        labels.push(`--${fname}${flagSignature(flags[fname])}`);
    }
    const colWidth = calculateColumnWidth(labels);

    if (positional.length > 0) {
        lines.push('Positional Arguments:');
        for (const posDef of positional) {
            const sig = posDef.required ? `<${posDef.name}>` : `[${posDef.name}]`;
            const label = `${sig} <${posDef.type}>`;
            lines.push(`  ${padRight(label, colWidth)} ${posDef.description}`);
        }
        lines.push('');
    }

    if (requiredFlagNames.length > 0) {
        lines.push('Required Flags:');
        for (const fname of requiredFlagNames) {
            const fdef = flags[fname];
            const label = `--${fname}${flagSignature(fdef)}`;
            lines.push(`  ${padRight(label, colWidth)} ${fdef.description}`);
        }
        lines.push('');
    }

    if (optionalFlagNames.length > 0) {
        lines.push('Optional Flags:');
        for (const fname of optionalFlagNames) {
            const fdef = flags[fname];
            const label = `--${fname}${flagSignature(fdef)}`;
            const suffix = ` (default: ${defaultRepr(fdef.default)})`;
            lines.push(`  ${padRight(label, colWidth)} ${fdef.description}${suffix}`);
        }
        lines.push('');
    }

    if (Array.isArray(contract.examples) && contract.examples.length > 0) {
        lines.push('Examples:');
        for (const example of contract.examples) {
            lines.push(`  ${example}`);
        }
        lines.push('');
    }

    while (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();

    return lines.join('\n');
}

module.exports = {
    generateGeneralHelp,
    generateCommandHelp,
    calculateColumnWidth
};
