import { spawnSync } from "node:child_process";

import { AbstractOutputFormat } from "./abstractOutputFormat.js";
import { isAscii } from "../utils/isAscii.js";

import type { I18nRow } from "../types/types.js";

class Pot extends AbstractOutputFormat {
    public output() {
        const input = this.items.join("\n");

        // Finally, write the POT file to a temporary directory, unique it and emit as webpack asset
        // eslint-disable-next-line n/no-sync
        const { stdout, stderr } = spawnSync("msguniq", [], {
            input,
            encoding: "utf-8",
        });

        if (stdout === null) {
            throw new Error(`msguniq: command not found, install with "apt-get install gettext"`);
        }

        if (stderr) {
            throw new Error(`msguniq: ${stderr}`);
        }

        return {
            output: stdout
                // msguniq creates a marker for duplicated content, we do not need this
                .replace(/#\. #-#-#-#-# {2}- {2}#-#-#-#-#\n/g, "")
                .trim(),
            added: stdout.match(/^msgid "/gm).length,
        };
    }

    public add(row: I18nRow) {
        if (this.items.length === 0) {
            this.items.push(`msgid ""
msgstr ""
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=utf-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Generated-By: @devowl-wp/regexp-translation-extractor\\n"

`);
        }

        row.comments.forEach(this.addComment);
        this.addOccurence(row.occurences);

        if (row.type === "singleContext" || row.type === "pluralContext") {
            this.addContext(row.context);
        }

        switch (row.type) {
            case "single":
                this.addMsgid(row.single);
                this.addMsgstr("");
                break;
            case "singleContext":
                this.addMsgid(row.single);
                this.addMsgstr("");
                break;
            case "plural":
                this.addMsgid(row.single);
                this.addMsgidPlural(row.plural);
                this.addMsgstrPlural([]);
                break;
            case "pluralContext":
                this.addMsgid(row.single);
                this.addMsgidPlural(row.plural);
                this.addMsgstrPlural([]);
                break;
            default:
                break;
        }

        this.items.push("\n");
    }

    protected addComment = (comment: string) => this.items.push(`#. ${comment}`);
    protected addOccurence = (occ: string) => this.items.push(`#: ${occ}`);
    protected addContext = (context: string) => this.items.push(`msgctxt ${JSON.stringify(context)}`);
    protected addMsgid = (id: string) => {
        if (!isAscii(id, true)) {
            // The following error can be thrown by `msgmerge` or `msgfmt` when using non-ASCII characters in the `msgid`
            throw new Error(
                `The following msgid contains non-ASCII characters. This will cause problems to translators who use a character encoding different from yours. Consider using a pure ASCII msgid instead: ${id}`,
            );
        }

        return this.items.push(`msgid ${JSON.stringify(id)}`);
    };
    protected addMsgidPlural = (id: string) => this.items.push(`msgid_plural ${JSON.stringify(id)}`);
    protected addMsgstr = (translation = "") => this.items.push(`msgstr ${JSON.stringify(translation)}`);
    protected addMsgstrPlural = (translations: string[]) => {
        if (!translations.length) {
            // 2 empty translations by default
            this.items.push('msgstr[0] ""');
            this.items.push('msgstr[1] ""');
        } else {
            translations.forEach((val, index) => this.items.push(`msgstr[${index}] ${JSON.stringify(val)}`));
        }
    };
}

export { Pot };
