const fs = require("fs");
const path = require("path");
const vm = require("vm");
const { spawnSync } = require("child_process");

const root = path.resolve(__dirname, "..");
const errors = [];

function read(file) {
    return fs.readFileSync(path.join(root, file), "utf8");
}

for (const file of ["app/js/app.js", "app/js/player.js", "app/js/config.js"]) {
    try {
        new vm.Script(read(file), { filename: file });
    } catch (error) {
        errors.push(`${file}: ${error.message}`);
    }
}

const appSource = read("app/js/app.js");
const functionNames = [...appSource.matchAll(/^    function\s+([A-Za-z0-9_]+)\s*\(/gm)].map((match) => match[1]);
const duplicates = functionNames.filter((name, index) => functionNames.indexOf(name) !== index);
if (duplicates.length) errors.push(`Shadowed top-level functions: ${[...new Set(duplicates)].join(", ")}`);
if (/[ÃÂØÙÐÑâäæà¤]/.test(appSource)) errors.push("app.js contains likely mojibake text");

const html = read("app/index.html");
const htmlIds = [...html.matchAll(/\bid="([^"]+)"/g)].map((match) => match[1]);
const duplicateIds = htmlIds.filter((id, index) => htmlIds.indexOf(id) !== index);
if (duplicateIds.length) errors.push(`Duplicate HTML IDs: ${[...new Set(duplicateIds)].join(", ")}`);
const referencedIds = [...appSource.matchAll(/getElementById\("([^"]+)"\)/g)].map((match) => match[1]);
const missingIds = [...new Set(referencedIds.filter((id) => !htmlIds.includes(id)))];
if (missingIds.length) errors.push(`DOM IDs missing from index.html: ${missingIds.join(", ")}`);

const config = read("backend/config.php");
if (/['"]pass['"]\s*=>\s*['"][^'"]{3,}/.test(config) || /sk_(?:live|test)_[A-Za-z0-9]/.test(config)) {
    errors.push("backend/config.php appears to contain a committed secret");
}

const php = spawnSync("php", ["--version"], { encoding: "utf8", shell: process.platform === "win32" });
if (php.status === 0) {
    const phpFiles = [];
    function walk(directory) {
        for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
            const full = path.join(directory, entry.name);
            if (entry.isDirectory()) walk(full);
            else if (entry.name.endsWith(".php")) phpFiles.push(full);
        }
    }
    walk(path.join(root, "backend"));
    for (const file of phpFiles) {
        const result = spawnSync("php", ["-l", file], { encoding: "utf8" });
        if (result.status !== 0) errors.push(result.stderr || result.stdout);
    }
} else {
    process.stdout.write("PHP is not installed; PHP syntax checks were skipped.\n");
}

if (errors.length) {
    process.stderr.write(errors.map((error) => `ERROR: ${error}`).join("\n") + "\n");
    process.exit(1);
}
process.stdout.write("Project validation passed.\n");
