import { parse as JSONC } from "https://deno.land/x/jsonct@v0.1.0/mod.ts";

type ConfigCheck = {path?:string, text?:string, json?:Record<string, string|Record<string, string|string[]>>};
type ConfigCheckPair = [config:ConfigCheck, imports:ConfigCheck];

export const RootHost = import.meta.resolve("./");
export const Root = new URL(`file://${Deno.cwd().replaceAll("\\", "/")}`).toString();
export async function HuntConfig()
{
    let path:string, resp:Response, text="", json;
    try
    {
        path = "deno.json"
        resp = await fetch(Root + "/" + path);
        text = await resp.text();
    }
    catch(e)
    {
        try
        {
            path = "deno.jsonc";
            resp = await fetch(Root + "/" + path);
            text = await resp.text();
        }
        catch(e)
        {
            try
            {
                path = ".vscode/settings.json";
                resp = await fetch(Root + "/" + path);
                json = await resp.json();

                path = json["deno.config"];
                json = undefined;
                if(path)
                {
                    resp = await fetch(Root + "/" + path);
                    text = await resp.text();
                }
            }
            catch(e)
            {
                path = "";
            }
        }
    }

    if(path)
    {
        try
        {
            json = JSONC(text);
        }
        catch(e)
        {
            json = undefined;
        }
    }

    let imports:ConfigCheck = {};
    if(json && json.imports)
    {
        // config.imports
        imports.json = json;
        imports.text = JSON.stringify(json);
        imports.path = path;
    }
    else if(json && !json.imports && json.importMap)
    {
        // config.importMap
        try
        {
            imports.path = json.importMap;
            resp = await fetch(Root + "/" + imports.path);
            imports.text = await resp.text();
            try
            {
                imports.json = JSONC(imports.text);
            }
            catch(e)
            {
                imports.json = undefined;
            }
        }
        catch(e)
        {
            // malformed import map
        }
    }

    return [{path, text, json}, imports] as ConfigCheckPair
}

export async function Install(file:string, overrideName?:string, handler?:(content:string)=>string)
{
    const pathFile = RootHost + "install__/" + file;

    try{
        const check = await Deno.readTextFile(Deno.cwd()+"/"+file);
        const replace = confirm(`⚠️🚧 The file "${file}" already exists. Replace it?`);
        if(replace)
        {
            throw("")
        }
        console.log(`Using pre-existing "${file}" for now.`);
    }
    catch(e)
    {
        const resp = await fetch(pathFile);
        const text = await resp.text();
        const name = overrideName || file;
        await Deno.writeTextFile(Deno.cwd()+"/"+name, handler ? handler(text) : text);   
    }
}

export async function Check()
{
    let [config, imports] = await HuntConfig();
    console.log(`Checking directory "${Root}"`);
    console.log("Found", config, imports);
    try
    {
        
        //console.log(config, imports);
        if(!config.path)
        {
            console.log(`🛠️ No Deno configuration found. Creating "deno.jsonc" now.`);
            await Deno.writeTextFile(Deno.cwd()+"/deno.jsonc", `{"imports":{}}`);   
            Check();
            return;
        }
        else if(!config.json)
        {
            if(confirm(`🚧 Deno configuration is malformed. Replace "${config.path}" with a new one?.`))
            {
                await Deno.writeTextFile(Deno.cwd()+"/"+config.path, `{"imports":{}}`);   
                Check();
                return;
            }
            else
            {
                throw("⛔ Invalid configuration.");
            }
        }
        else if(!imports.json)
        {        
            if(imports.path != config.path)
            {
                if(confirm(`🚧 External import map "${imports.path}" is missing or malformed. Replace it with defaults?.`))
                {
                    await Deno.writeTextFile(Deno.cwd()+"/"+imports.path, `{"imports":{}}`);   
                    Check();
                    return;
                }
                else
                {
                    throw("⛔ Invalid configuration.");
                }
            }
        }
        else if(!imports.json?.imports)
        {
            imports.json.imports = {};
        }

        if(config.json && imports.json?.imports)
        {
            const importMap = imports.json.imports as Record<string, string>;
            const bake =async(obj:ConfigCheck)=> await Deno.writeTextFile(Deno.cwd()+"/"+obj.path, JSON.stringify(obj.json, null, "\t")); 

            if(!importMap["react"])
            {
                console.log(`🛠️ Adding React import specifier ("react")`);
                importMap["react"] = `https://esm.sh/preact@10.16.0/compat`;
                importMap["react/"] = `https://esm.sh/preact@10.16.0/compat/`;
                await bake(imports);
                console.log(`🚦 NOTE: Deno will need to cache "react" for intellisense to work properly.`)

            }
            if(!importMap[">able/"])
            {
                console.log(`🛠️ Adding Able import specifier (">able/").`);
                importMap[">able/"] = `${RootHost}`;
                await bake(imports); 
            }

            const tasks:Record<string, string> = {
                "check": `deno run -A --no-lock ${RootHost}cli.tsx check`,
                "local": `deno run -A --no-lock ${RootHost}cli.tsx local`,
                "debug": `deno run -A --no-lock ${RootHost}cli.tsx debug`,
                "serve": `deno run -A --no-lock ${RootHost}cli.tsx serve`,
                "cloud": `deno run -A --no-lock ${RootHost}cli.tsx cloud`        
            };

            const confTasks = (config.json.tasks || {}) as Record<string, string>;
            for(const key in tasks)
            {
                if(tasks[key] !== confTasks[key])
                {
                    if(confirm(`🤔 OPTIONAL: The tasks defined in your config contain missing or modified values. Update tasks?`))
                    {
                        config.json.tasks = {...confTasks, ...tasks};
                        await bake(config);
                    }
                    break;
                }
            }


            const options = 
            {
                "lib": ["deno.window", "dom", "dom.asynciterable"],
                "jsx": "react-jsx",
                "jsxImportSource": "react"
            }

            const compOpts = config.json.compilerOptions as Record<string, string|string[]> || {};
            const compJSX = compOpts.jsx == options.jsx;
            const compJSXImportSource = compOpts.jsxImportSource == options.jsxImportSource;
            const compLib:string[] = compOpts.lib as string[] || [];
            let compLibHasAll = true;
            options.lib.forEach(item=> !compLib.includes(item) && (compLibHasAll = false))

            if(!compOpts || !compJSX || !compJSXImportSource || !compLibHasAll)
            {
                console.log(`🛠️ Adding values to "compilerOptions" configuration.`);
                compOpts.jsx = options.jsx;
                compOpts.jsxImportSource = options.jsxImportSource;
                compOpts.lib = [...compLib, ...options.lib];
                config.json.compilerOptions = compOpts;
                await bake(config);
            }


            if(!importMap[">able/app.tsx"])
            {
                if(confirm(`🤔 OPTIONAL: Your import map does not override the default/empty FRONT-END app with the specifier ">able/app.tsx". Create this file and add the specifier?`))
                {
                    importMap[">able/app.tsx"] = `./app.tsx`;
                    await bake(imports); 
                    await Install("app.tsx");
                }
            }
            else
            {
                /*
                try
                {
                    const app = await import(importMap[">able/app.tsx"]);
                    // @ts-ignore
                    const result = app.default().$$typeof;
                }
                catch(e)
                {
                    console.log(e);
                    if(confirm(`🚧 Your FRONT-END app ("${importMap[">able/app.tsx"]}") does not export a default function that returns VDOM nodes. Replace it?`))
                    {
                        await Install("app.tsx", importMap[">able/app.tsx"]);
                    }
                    else
                    {
                        throw("⛔ Your FRONT-END app has incorrect export types.");
                    }
                }
                */
            }

            if(!importMap[">able/api.tsx"])
            {
                if(confirm(`🤔 OPTIONAL: Your import map does not override the default/empty BACK-END api with the specifier ">able/api.tsx". Create this file and add the specifier?`))
                {
                    importMap[">able/api.tsx"] = "./api.tsx";
                    await bake(imports); 
                    await Install("api.tsx");
                }
            }
            else
            {
                /*
                try
                {
                    const api = await import(importMap[">able/api.tsx"]);
                    const result = api.default(new Request(new URL("https://fake-deno-testing-domain.com/")));
                }
                catch(e)
                {
                    if(confirm(`🚧 Your starter backend app ("${importMap[">able/api.tsx"]}") does not export a default function that accepts a Request. Replace it?`))
                    {
                        await Install("api.tsx", importMap[">able/api.tsx"]);
                    }
                    else
                    {
                        throw("⛔ Starter backend app has incorrect export types.");
                    }
                }
                */
            }

        }
    }
    catch(e)
    {
        console.log(e, "\n (Able Exiting...)");
        Deno.exit();
    }
    console.log(`🚗 Good to go!`);

}