
import { Issue } from "./githubType.ts";
import { copy } from "https://deno.land/std@0.136.0/fs/mod.ts";
import { readAll } from "https://deno.land/std@0.135.0/streams/mod.ts"
import { parse as argParse } from "https://deno.land/std@0.135.0/flags/mod.ts";
import {
    normalize, join as pathJoin, fromFileUrl, parse as parsePath
    , relative
} from "https://deno.land/std@0.135.0/path/mod.ts";
import * as Eta from "https://deno.land/x/eta@v1.12.3/mod.ts";
import { createReactive } from "./reactivity.ts";

async function readContent(path?: string): Promise<string> {
    let content = "[]";
    if (path) {
        content = await Deno.readTextFile(path);
    }
    else if (!Deno.isatty(Deno.stdin.rid)) {
        const decoder = new TextDecoder(undefined, { ignoreBOM: true });
        const buf = await readAll(Deno.stdin);
        content = decoder.decode(buf);
    }
    else throw new Error("No input provided. path or stdin.");
    return content;
}
type printDocParam = {
    target: string,
    data: {
        issues: Issue[]
    }
};

async function printDoc(param: printDocParam, option?: {
    outDir?: string
}) {
    option = option ?? {};
    const { target, data } = param;
    const { outDir } = option;

    let print: string = "";
    print = await Eta.renderFile(target, data) as string;
    if (outDir) {
        const outPath = pathJoin(outDir, target);
        await Deno.mkdir(pathJoin(outDir), { recursive: true });
        await Deno.writeTextFile(outPath, print);
    }
    else {
        console.log(print);
    }
}

async function main() {
    const parsedArg = argParse(Deno.args);
    const { issue_path, outDirArg, w, watch } = parsedArg;
    const watchMode = w || watch;
    if (typeof issue_path !== "undefined" && typeof issue_path !== "string") {
        console.log("Please provide a path to the json file.");
        Deno.exit(1);
    }
    if (typeof outDirArg !== "undefined" && typeof outDirArg !== "string") {
        console.log("Please provide a path to the output file.");
        Deno.exit(1);
    }
    const outDir = (outDirArg ?? "build");
    if (typeof watchMode !== "undefined" && typeof watchMode !== "boolean") {
        console.log("Please provide a boolean value for w.");
        Deno.exit(1);
    }
    if (watchMode && typeof issue_path === "undefined") {
        console.log("Could not set watch mode without a path.");
        Deno.exit(1);
    }

    const url = new URL(import.meta.url)
    url.pathname = normalize(pathJoin(url.pathname, "..", "template"));
    const viewPath = fromFileUrl(url);
    Eta.configure({
        views: viewPath,
        "view cache": false,
    });
    const issuesR = await createReactive(async () => {
        const c = await readContent(issue_path);
        const issues = JSON.parse(c) as Issue[];
        issues.sort((a, b) => a.number - b.number);
        return issues;
    });

    const targets = ["SUMMARY.md", "overall.md", "specific.md", "intro.md", "support.md", "architecture.md"];

    const targetsR = await Promise.all(targets.map(async (t) => {
        return await createReactive(async () => {
            await printDoc({
                target: t, data: {
                    issues: issuesR.value
                }
            }, { outDir: outDir });
        });
    }

    ));
    issuesR.wireTo(...targetsR);
    const copyOp = await createReactive(async () => {
        const files = [...Deno.readDirSync(viewPath)].map(x => x.name).filter(x => !x.endsWith(".md"));
        const op = files.map(x => copy(pathJoin(viewPath, x), pathJoin(outDir, x), { overwrite: true }));
        await Promise.all(op);
    });

    if (watchMode) {
        const watcher = Deno.watchFs([viewPath, issue_path as string]);
        for await (const event of watcher) {
            if (event.kind === "modify") {
                Deno.stdout.write(
                    new TextEncoder().encode("\x1b[2J\x1b[0f"),
                );
                console.log(`reloading ${event.paths.join(", ")}`);
                for (const path of event.paths) {
                    const p = parsePath(path);
                    if (p.dir === viewPath) {
                        if (p.ext === ".md") {
                            targetsR[targets.indexOf(p.base)].update();
                        }
                        else {
                            copyOp.update();
                        }
                    }
                    else if (p.base === "issues.json") {
                        await issuesR.update();
                    }
                }
            }
        }
    }
}

if (import.meta.main) {
    main();
}