// The version, and the rule that there is only one of it.
//
// Three files declare a version and none of them can be derived from another:
// Chrome and Firefox each need their own manifest, both are copied to dist/
// verbatim (tests/manifest.test.js asserts that what is in manifest/ is what
// ships), and package.json's copy is what build.js compiles into the About
// screen. So the single source of truth is enforced rather than generated —
// they must all agree or there is no version and no build.
//
// Reading one of the three and ignoring the rest is what this replaces. That
// shape cannot fail: it silently ships an extension whose About screen and
// whose browser-reported version disagree, and whose release artifact is named
// after whichever file the packager happened to read.
//
// Required by build.js, script/lib/package.js and tests/version.test.js, so
// the build, the release artifacts and make check all apply the same rule to
// the same files.

"use strict";

const fs = require("fs");
const path = require("path");

const VERSION_SOURCES = [
    "package.json",
    "manifest/chrome.json",
    "manifest/firefox.json",
];

// Every declared version, in VERSION_SOURCES order, as { source, version }.
// A file that declares nothing usable fails here rather than being skipped:
// a missing version is not agreement.
function declaredVersions(root) {
    return VERSION_SOURCES.map((source) => {
        const file = path.join(root, source);
        let parsed;
        try {
            parsed = JSON.parse(fs.readFileSync(file, "utf8"));
        } catch (e) {
            throw new Error(
                `${source} could not be read as JSON: ${e.message}`,
            );
        }
        const version = parsed.version;
        if (typeof version !== "string" || version.trim() === "") {
            throw new Error(
                `${source} declares no usable "version" (found ` +
                    `${JSON.stringify(version)}). Every artifact is named and ` +
                    `stamped with it, so there is nothing to build without it.`,
            );
        }
        return { source, version };
    });
}

// The one version all three declare, or a failure naming every disagreeing
// file and what it said.
function resolveVersion(root) {
    const declared = declaredVersions(root);
    const distinct = [...new Set(declared.map((d) => d.version))];
    if (distinct.length !== 1) {
        throw new Error(
            "the declared versions disagree, so this tree has no version: " +
                declared.map((d) => `${d.source}=${d.version}`).join(", ") +
                ". Set all of them to the same value: the manifests are what " +
                "the browser reports and package.json is what the About " +
                "screen shows, and a build that picked one of them would " +
                "ship the disagreement.",
        );
    }
    return distinct[0];
}

module.exports = { VERSION_SOURCES, declaredVersions, resolveVersion };
