import * as fs from "fs";
import * as path from "path";
import { DIAGNOSTIC_CATALOG } from "../core/src/diagnostics/diagnosticCatalog.js";
import { getContributedSettingKeys, getRuleSettingKeyByRuleId } from "./shared/settings-metadata.js";

import { fileURLToPath } from "url";
import { dirname } from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const ROOT_DIR = path.join(__dirname, "..");
const SOURCE_DIRS = [
    path.join(ROOT_DIR, "src"),
    path.join(ROOT_DIR, "extension.js")
];
const NON_CONFIGURABLE_KEYS = new Set([
    "acurity.templates.dontAskAgain",
    "acurity.templates.authorPromptShown"
]);

function getJavaScriptFiles(entryPath) {
    if (!fs.existsSync(entryPath)) {
        return [];
    }

    const stats = fs.statSync(entryPath);
    if (stats.isFile()) {
        return entryPath.endsWith(".js") ? [entryPath] : [];
    }

    const files = [];
    for (const dirEntry of fs.readdirSync(entryPath, { withFileTypes: true })) {
        const fullPath = path.join(entryPath, dirEntry.name);
        if (dirEntry.isDirectory()) {
            if (dirEntry.name === "node_modules" || dirEntry.name === "dist") {
                continue;
            }
            files.push(...getJavaScriptFiles(fullPath));
        } else if (dirEntry.name.endsWith(".js")) {
            files.push(fullPath);
        }
    }

    return files;
}

function collectUsedSettingsFromContent(content) {
    const keys = new Set();

    const fullKeyPattern = /["'`](acurity\.[A-Za-z0-9_.-]+)["'`]/g;
    const nestedPattern = /getConfiguration\(\s*["'`](acurity(?:\.[A-Za-z0-9_-]+)*)["'`]\s*\)\s*\.(?:get|update)\(\s*["'`]([A-Za-z0-9_.-]+)["'`]/g;

    let match = null;
    while ((match = fullKeyPattern.exec(content)) !== null) {
        const key = match[1];
        if (key.startsWith("acurity.") && !key.startsWith("acurity." + "runSetupWizard")) {
            keys.add(key);
        }
    }

    while ((match = nestedPattern.exec(content)) !== null) {
        const namespace = match[1];
        const subKey = match[2];
        keys.add(`${namespace}.${subKey}`);
    }

    return keys;
}

function isConfigurableSettingKey(key) {
    return (
        key.startsWith("acurity.rules.") ||
        key.startsWith("acurity.naming.") ||
        key.startsWith("acurity.robodoc.") ||
        key.startsWith("acurity.templates.") ||
        key.startsWith("acurity.diagnostics.") ||
        key === "acurity.logging"
    );
}

function validateSettingsRegistration() {
    const contributedSettings = new Set(getContributedSettingKeys());
    const ruleSettingByRuleId = getRuleSettingKeyByRuleId();
    const usedSettings = new Set();

    for (const sourceEntry of SOURCE_DIRS) {
        for (const jsFile of getJavaScriptFiles(sourceEntry)) {
            const content = fs.readFileSync(jsFile, "utf8");
            for (const key of collectUsedSettingsFromContent(content)) {
                if (isConfigurableSettingKey(key) && !NON_CONFIGURABLE_KEYS.has(key)) {
                    usedSettings.add(key);
                }
            }
        }
    }

    const missing = Array.from(usedSettings)
        .filter((key) => !contributedSettings.has(key))
        .sort((a, b) => a.localeCompare(b));

    if (missing.length === 0) {
        console.log("All configurable settings used in source are registered in package.json.");
    } else {
        console.error("Configurable settings used in source but missing from package.json:");
        missing.forEach((key) => console.error(`- ${key}`));
    }

    const catalogRuleIds = Array.from(new Set(Object.values(DIAGNOSTIC_CATALOG).map((entry) => entry.ruleId))).sort();
    const ruleIdsMissingSetting = catalogRuleIds
        .filter((ruleId) => !ruleSettingByRuleId[ruleId]);

    if (ruleIdsMissingSetting.length === 0) {
        console.log("All diagnostic rule IDs are represented by contributed severity settings in package.json.");
    } else {
        console.error("Diagnostic rule IDs missing contributed severity settings in package.json:");
        ruleIdsMissingSetting.forEach((ruleId) => console.error(`- ${ruleId}`));
    }

    return (missing.length === 0 && ruleIdsMissingSetting.length === 0) ? 0 : 1;
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
    process.exitCode = validateSettingsRegistration();
}

export {
    validateSettingsRegistration
};
