'use strict';

const BOOLEAN_TRUE = new Set(['true', '1', 'yes']);
const BOOLEAN_FALSE = new Set(['false', '0', 'no']);

function coerceValue(rawValue, type) {
    if (type === 'string') {
        return { value: String(rawValue), error: null };
    }

    if (type === 'number') {
        const parsed = parseFloat(rawValue);
        if (Number.isNaN(parsed)) {
            return { value: undefined, error: `expected number, got ${JSON.stringify(rawValue)}` };
        }
        return { value: parsed, error: null };
    }

    if (type === 'boolean') {
        if (rawValue === true || rawValue === false) {
            return { value: rawValue, error: null };
        }
        const lower = String(rawValue).toLowerCase();
        if (BOOLEAN_TRUE.has(lower)) return { value: true, error: null };
        if (BOOLEAN_FALSE.has(lower)) return { value: false, error: null };
        return { value: undefined, error: `expected boolean (true|false|1|0|yes|no), got ${JSON.stringify(rawValue)}` };
    }

    return { value: rawValue, error: null };
}

function hasHelpFlag(argv) {
    return argv.some((a) => a === '--help' || a === '-h');
}

function parseArgs(argv, contract) {
    if (!Array.isArray(argv)) {
        throw new Error('parseArgs: argv must be an array');
    }
    if (!contract || typeof contract !== 'object') {
        throw new Error('parseArgs: contract must be an object');
    }

    if (hasHelpFlag(argv)) {
        return { args: { _help: true }, errors: [] };
    }

    const flags = contract.flags || {};
    const positionalDefs = Array.isArray(contract.positional) ? contract.positional : [];
    const args = {};
    const errors = [];
    const seenFlags = new Set();
    const positionalValues = [];

    let i = 0;
    while (i < argv.length) {
        const token = argv[i];

        if (token === '--') {
            for (let j = i + 1; j < argv.length; j++) positionalValues.push(argv[j]);
            break;
        }

        if (token.startsWith('--')) {
            const body = token.slice(2);
            let flagName;
            let rawValue;
            let valueProvided = false;

            const eqIdx = body.indexOf('=');
            if (eqIdx >= 0) {
                flagName = body.slice(0, eqIdx);
                rawValue = body.slice(eqIdx + 1);
                valueProvided = true;
            } else {
                flagName = body;
            }

            if (flagName.startsWith('no-') && flags[flagName.slice(3)] && flags[flagName.slice(3)].type === 'boolean') {
                const realName = flagName.slice(3);
                if (valueProvided) {
                    errors.push(`Flag --no-${realName} does not accept a value`);
                } else {
                    args[realName] = false;
                    seenFlags.add(realName);
                }
                i += 1;
                continue;
            }

            const flagDef = flags[flagName];
            if (!flagDef) {
                errors.push(`Unknown flag: --${flagName}`);
                if (!valueProvided && i + 1 < argv.length && !argv[i + 1].startsWith('--')) {
                    i += 2;
                } else {
                    i += 1;
                }
                continue;
            }

            if (flagDef.type === 'boolean') {
                if (!valueProvided) {
                    args[flagName] = true;
                    seenFlags.add(flagName);
                    i += 1;
                    continue;
                }
                const coerced = coerceValue(rawValue, 'boolean');
                if (coerced.error) {
                    errors.push(`Invalid value for --${flagName}: ${coerced.error}`);
                } else {
                    args[flagName] = coerced.value;
                    seenFlags.add(flagName);
                }
                i += 1;
                continue;
            }

            if (!valueProvided) {
                if (i + 1 >= argv.length || argv[i + 1].startsWith('--')) {
                    errors.push(`Flag --${flagName} requires a value`);
                    i += 1;
                    continue;
                }
                rawValue = argv[i + 1];
                i += 2;
            } else {
                i += 1;
            }

            const coerced = coerceValue(rawValue, flagDef.type);
            if (coerced.error) {
                errors.push(`Invalid value for --${flagName}: ${coerced.error}`);
                continue;
            }
            args[flagName] = coerced.value;
            seenFlags.add(flagName);
            continue;
        }

        positionalValues.push(token);
        i += 1;
    }

    for (const [name, def] of Object.entries(flags)) {
        if (seenFlags.has(name)) continue;
        if (def.required) {
            errors.push(`Missing required flag: --${name}`);
            args[name] = undefined;
        } else {
            args[name] = def.default;
        }
    }

    positionalDefs.forEach((posDef, idx) => {
        const rawValue = positionalValues[idx];
        if (rawValue === undefined) {
            if (posDef.required) {
                errors.push(`Missing required positional argument: <${posDef.name}>`);
                args[posDef.name] = undefined;
            } else {
                args[posDef.name] = undefined;
            }
            return;
        }
        const coerced = coerceValue(rawValue, posDef.type);
        if (coerced.error) {
            errors.push(`Invalid value for positional <${posDef.name}>: ${coerced.error}`);
            args[posDef.name] = undefined;
            return;
        }
        args[posDef.name] = coerced.value;
    });

    if (positionalValues.length > positionalDefs.length) {
        const extras = positionalValues.slice(positionalDefs.length);
        for (const extra of extras) {
            errors.push(`Unexpected positional argument: ${extra}`);
        }
    }

    return { args, errors };
}

module.exports = {
    parseArgs
};
