'use strict';

const KEBAB_CASE_RE = /^[a-z][a-z0-9-]*$/;
const VALID_CATEGORIES = ['generation', 'management', 'introspection', 'utility'];
const VALID_FLAG_TYPES = ['string', 'number', 'boolean'];
const VALID_POSITIONAL_TYPES = ['string', 'number'];

function isPlainObject(value) {
    return value !== null && typeof value === 'object' && !Array.isArray(value);
}

function isNonEmptyString(value) {
    return typeof value === 'string' && value.trim().length > 0;
}

function throwInvalid(filePath, fieldPath, reason) {
    throw new Error(`Invalid contract in ${filePath}: ${fieldPath} ${reason}`);
}

function validateFlag(flagName, flagDef, filePath) {
    const base = `flags.${flagName}`;

    if (!isPlainObject(flagDef)) {
        throwInvalid(filePath, base, 'must be a plain object');
    }

    if (!VALID_FLAG_TYPES.includes(flagDef.type)) {
        throwInvalid(
            filePath,
            `${base}.type`,
            `must be one of 'string', 'number', 'boolean', got ${JSON.stringify(flagDef.type)}`
        );
    }

    if (typeof flagDef.required !== 'boolean') {
        throwInvalid(filePath, `${base}.required`, 'must be a boolean');
    }

    const hasDefault = Object.prototype.hasOwnProperty.call(flagDef, 'default');
    if (flagDef.required === true && hasDefault) {
        throwInvalid(filePath, `${base}.default`, 'must NOT be present when required=true');
    }
    if (flagDef.required === false && !hasDefault) {
        throwInvalid(filePath, `${base}.default`, 'is required when required=false (use null for no default)');
    }

    if (!isNonEmptyString(flagDef.description)) {
        throwInvalid(filePath, `${base}.description`, 'must be a non-empty string');
    }
}

function validatePositional(index, posDef, filePath) {
    const base = `positional[${index}]`;

    if (!isPlainObject(posDef)) {
        throwInvalid(filePath, base, 'must be a plain object');
    }

    if (!isNonEmptyString(posDef.name) || !KEBAB_CASE_RE.test(posDef.name)) {
        throwInvalid(
            filePath,
            `${base}.name`,
            `must be a non-empty kebab-case string, got ${JSON.stringify(posDef.name)}`
        );
    }

    if (!VALID_POSITIONAL_TYPES.includes(posDef.type)) {
        throwInvalid(
            filePath,
            `${base}.type`,
            `must be one of 'string', 'number', got ${JSON.stringify(posDef.type)}`
        );
    }

    if (typeof posDef.required !== 'boolean') {
        throwInvalid(filePath, `${base}.required`, 'must be a boolean');
    }

    if (!isNonEmptyString(posDef.description)) {
        throwInvalid(filePath, `${base}.description`, 'must be a non-empty string');
    }
}

function validateContract(contract, filePath) {
    if (!isPlainObject(contract)) {
        throwInvalid(filePath || '<unknown>', '<root>', 'must be a plain object exported via module.exports');
    }

    const fp = filePath || '<unknown>';

    if (contract.resource !== null && contract.resource !== undefined) {
        if (!isNonEmptyString(contract.resource) || !KEBAB_CASE_RE.test(contract.resource)) {
            throwInvalid(
                fp,
                'resource',
                `must be a non-empty kebab-case string starting with a letter, got ${JSON.stringify(contract.resource)}`
            );
        }
    }

    if (!isNonEmptyString(contract.verb) || !KEBAB_CASE_RE.test(contract.verb)) {
        if (contract.verb === undefined || contract.verb === null) {
            throwInvalid(fp, 'verb', 'is required');
        }
        throwInvalid(
            fp,
            'verb',
            `must be a non-empty kebab-case string starting with a letter, got ${JSON.stringify(contract.verb)}`
        );
    }

    if (!isNonEmptyString(contract.description)) {
        throwInvalid(fp, 'description', 'must be a non-empty string');
    }

    if (!VALID_CATEGORIES.includes(contract.category)) {
        throwInvalid(
            fp,
            'category',
            `must be one of ${VALID_CATEGORIES.map((c) => `'${c}'`).join(', ')}, got ${JSON.stringify(contract.category)}`
        );
    }

    if (!isPlainObject(contract.flags)) {
        throwInvalid(fp, 'flags', 'must be a plain object (use {} for no flags)');
    }

    for (const flagName of Object.keys(contract.flags)) {
        if (!KEBAB_CASE_RE.test(flagName)) {
            throwInvalid(
                fp,
                `flags.${flagName}`,
                `name must be kebab-case starting with a letter, got ${JSON.stringify(flagName)}`
            );
        }
        validateFlag(flagName, contract.flags[flagName], fp);
    }

    if (contract.positional !== undefined && contract.positional !== null) {
        if (!Array.isArray(contract.positional)) {
            throwInvalid(fp, 'positional', 'must be an array (or omitted)');
        }
        contract.positional.forEach((posDef, index) => {
            validatePositional(index, posDef, fp);
        });
    }

    if (contract.examples !== undefined && contract.examples !== null) {
        if (!Array.isArray(contract.examples)) {
            throwInvalid(fp, 'examples', 'must be an array of non-empty string (or omitted)');
        }
        contract.examples.forEach((example, index) => {
            if (!isNonEmptyString(example)) {
                throwInvalid(fp, `examples[${index}]`, 'must be a non-empty string');
            }
        });
    }

    if (typeof contract.handler !== 'function') {
        throwInvalid(fp, 'handler', 'is required and must be a function (sync or async)');
    }
}

module.exports = {
    validateContract,
    KEBAB_CASE_RE,
    VALID_CATEGORIES,
    VALID_FLAG_TYPES,
    VALID_POSITIONAL_TYPES
};
