const { 
    appPackageFolder, 
    appsFolder 
} = require("../createAppPackage");
const { 
    readJSON,
    writeJSON,
    readdir,
    stat,
    ensureDir,
    rename,
    unlink,
    rmdir
} = require("fs-extra");
const { 
    join,
    dirname
} = require("path");
const { $ } = require("@budibase/core").common;
const { 
    keyBy
} = require("lodash/fp");
const {merge} = require("lodash");

const { componentLibraryInfo } = require("./componentLibraryInfo");
const savePackage = require("./savePackage");

module.exports.savePackage = savePackage;

module.exports.getPackageForBuilder = async (config, appname) => {
    const appPath = appPackageFolder(config, appname);

    const pages = await readJSON(`${appPath}/pages.json`);

    return ({
        appDefinition: await readJSON(`${appPath}/appDefinition.json`),

        accessLevels: await readJSON(`${appPath}/access_levels.json`),

        pages,

        rootComponents: await getRootComponents(appPath, pages),

        derivedComponents: keyBy("name")(
            await fetchDerivedComponents(appPath))
    });

}



module.exports.getApps = async (config) => 
    await readdir(appsFolder(config));


const componentPath = (appPath, name) =>
    join(appPath, "components", name + ".json");

module.exports.saveDerivedComponent = async (config, appname, component) => {
    const appPath = appPackageFolder(config, appname);
    const compPath = componentPath(appPath, component.name);
    await ensureDir(dirname(compPath));
    await writeJSON(
        compPath, 
        component,
        {encoding:"utf8", flag:"w", spaces:2});
}

module.exports.renameDerivedComponent = async (config, appname, oldName, newName) => {
    const appPath = appPackageFolder(config, appname);

    const oldComponentPath = componentPath(
        appPath, oldName);

    const newComponentPath = componentPath(
        appPath, newName);

    await ensureDir(dirname(newComponentPath));
    await rename(
        oldComponentPath, 
        newComponentPath);    
}

module.exports.deleteDerivedComponent = async (config, appname, name) => {
    const appPath = appPackageFolder(config, appname);
    const componentFile = componentPath(appPath, name);
    await unlink(componentFile);

    const dir = dirname(componentFile);
    if((await readdir(dir)).length === 0) {
        await rmdir(dir);
    }
}

module.exports.componentLibraryInfo = async (config, appname, lib) => {
    const appPath = appPackageFolder(config, appname);
    return await componentLibraryInfo(appPath, lib);
};


const getRootComponents = async (appPath, pages ,lib) => {

    let libs;
    if(!lib) {
        pages = pages || await readJSON(
            `${appPath}/pages.json`);

        if(!pages.componentLibraries) return [];

        libs = pages.componentLibraries;
    } else {
        libs = [lib];
    }

    const components = {};
    for(let l of libs) {
        const info = await componentLibraryInfo(appPath, l);
        merge(components, info.components);
    }

    if(components._lib) delete components._lib;
    
    return components;
}

const fetchDerivedComponents = async (appPath, relativePath = "") => {
    
    const currentDir = join(appPath, "components", relativePath);

    const contents = await readdir(currentDir);

    const components = [];

    for(let item of contents) {
        const itemRelativePath = join(relativePath, item);
        const itemFullPath = join(currentDir, item);
        const stats = await stat(itemFullPath);

        if(stats.isFile()) {
            
            if(!item.endsWith(".json")) continue;

            const component = 
                await readJSON(itemFullPath);

            component.name = itemRelativePath
                                .substring(0, itemRelativePath.length - 5)
                                .replace(/\\/g, "/");

            component.props = component.props || {};

            components.push(component);
        } else {
            const childComponents = await fetchDerivedComponents(
                appPath, join(relativePath, item)
            );

            for(let c of childComponents) {
                components.push(c);
            }
        }
    }

    return components;
}

module.exports.getRootComponents = getRootComponents;