'use strict';

const fs = require('node:fs');
const path = require('node:path');
const { validateContract } = require('./contract-validator');

const SKIP_FOLDERS = new Set(['lib']);

function isJsCommandFile(name) {
    if (!name.endsWith('.js')) return false;
    if (name.endsWith('.test.js')) return false;
    if (name.startsWith('_')) return false;
    return true;
}

function isResourceFolder(name) {
    if (name.startsWith('_')) return false;
    if (SKIP_FOLDERS.has(name)) return false;
    return true;
}

function discoverCommands(cliDir) {
    const manifest = {
        resources: {},
        globalVerbs: {}
    };

    if (!cliDir || typeof cliDir !== 'string') {
        throw new Error('discoverCommands: cliDir must be a non-empty string');
    }

    if (!fs.existsSync(cliDir)) {
        return manifest;
    }

    const stat = fs.statSync(cliDir);
    if (!stat.isDirectory()) {
        throw new Error(`discoverCommands: cliDir is not a directory: ${cliDir}`);
    }

    const rootEntries = fs.readdirSync(cliDir, { withFileTypes: true });

    for (const entry of rootEntries) {
        if (entry.isFile() && isJsCommandFile(entry.name)) {
            const verb = entry.name.slice(0, -3);
            manifest.globalVerbs[verb] = path.resolve(cliDir, entry.name);
            continue;
        }

        if (entry.isDirectory() && isResourceFolder(entry.name)) {
            const resourceName = entry.name;
            const resourceDir = path.join(cliDir, resourceName);
            const resourceEntries = fs.readdirSync(resourceDir, { withFileTypes: true });

            const verbs = {};
            for (const sub of resourceEntries) {
                if (sub.isFile() && isJsCommandFile(sub.name)) {
                    const verb = sub.name.slice(0, -3);
                    verbs[verb] = path.resolve(resourceDir, sub.name);
                }
            }

            if (Object.keys(verbs).length > 0) {
                manifest.resources[resourceName] = verbs;
            }
        }
    }

    return manifest;
}

const loadCache = new Map();

function loadCommand(filePath) {
    if (!filePath || typeof filePath !== 'string') {
        throw new Error('loadCommand: filePath must be a non-empty string');
    }

    if (loadCache.has(filePath)) {
        return loadCache.get(filePath);
    }

    if (!fs.existsSync(filePath)) {
        throw new Error(`loadCommand: file not found: ${filePath}`);
    }

    let contract;
    try {
        delete require.cache[require.resolve(filePath)];
        contract = require(filePath);
    } catch (err) {
        throw new Error(`loadCommand: failed to require ${filePath}: ${err.message}`);
    }

    validateContract(contract, filePath);
    loadCache.set(filePath, contract);
    return contract;
}

function clearCache() {
    loadCache.clear();
}

module.exports = {
    discoverCommands,
    loadCommand,
    clearCache
};
