// @cerxes
import { host, Host } from "@cerxes/host";

// Rollup and plugins
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';

// Proc args (this is weird to be here, but it is the same for build/watch, and should be kept in sync as much as possible
import process from "process";
import * as rollup from "rollup";
import { logBundle } from "./log-bundle";
let args = process.argv.slice(2);
const runArgSeperator = args.indexOf('--');
export let buildTargets = ['lib-es', 'lib-cjs', 'dist-es', 'dist-es-min', 'dist-cjs', 'dist-cjs-min'];
if (runArgSeperator >= 0) {
    buildTargets = args.slice(runArgSeperator + 1);
    args = args.slice(0, runArgSeperator);
}

export class CsxConfig {
    constructor({
        src,
        dist,
        root,
        opts
    } = {}) {
        const { watch } = opts || {};

        this.root = root ? host.from(root) : host.from('.');// (This host.from('.') would not've been necessary if @cerxes/host did not have a bug on this one)
        this.src = this.root.from(src || 'src');
        this.dist = this.root.from(dist || 'dist');
        this.lib = this.root.from(dist || 'lib');
    }

    /** @type {Host} */
    src;
    /**
     *  Directory to use for dist output (single bundle)
     *  @type {Host} */
    dist;
    /**
     *  Directory to use for library output (transpiled, file by file)
     *  @type {Host} */
    lib;

    /** @type {string} */
    sourceGlob = "**/*.@(*.js|js)";

    /**
     * Get rollup-config to compile a source file
     * @param {string} file
     * @param {string} out
     * @param {object} [opts]
     * @param {boolean} [opts.es] - Export in es6 format (or cjs when false)
     * @param {boolean} [opts.single] - Compile single file (e.g. don't follow imports)
     * @param {boolean} [opts.sourcemap] - Include sourcemaps
     * @param {boolean} [opts.minified] - Minify the output (e.g. Terser-plugin)
     * @returns {{output: {file: string, sourcemap: boolean, plugins: Array<Object>, format: string}, input: string, plugins: ...*[]}}
     */
    srcCfg = (file, out = undefined, opts) => {
        let inputFile = this.src.resolve(file);
        let outputFile = this.dist.resolve(out || file);
        return ({
            // Compile the actual app
            input: inputFile,
            output: {
                file: outputFile,
                format: opts?.es? 'es' : 'cjs', // NodeJS
                sourcemap: opts?.sourcemap ?? true,
            },
            external: (id, parentId, isResolved) => {
                if (!id || id[0] === '\0') return false;// Special cases
                else {
                    let split = (id || "").split('/');
                    let first = id[0] === '@' ? split.slice(0, 2).join('/') : split[0];
                    let absId = first === '..' || first === '.' ? this.src.resolve(parentId, id) : id;
                    if (absId) {
                        if(absId.indexOf(this.src.workingDirectory) >= 0 && opts?.single) return true; // Own source-file => external when single mode enabled
                    }
                    // Any other unresolved identifier(which should be resolved further)
                    return false; // The light, inline them
                }
            },
            treeshake: !opts?.single,
            plugins: [
                // babel
                babel({
                    babelrc: true,
                }),
                // NodeJS-style resolving (Its a shame this is not a Rollup-default, we need this for index.js files to work!)
                resolve({
                    extensions: ['.mjs', '.js', '.jsx', '.json'],
                    preferBuiltins: true, mainFields: ['browser']
                }),
                opts?.minified ? terser() : null
            ].filter(t=>t)
        })
    };
    
    /**
     * @param {string} sources
     * @returns {TargetConfig[]}
     */
    configureTargets = (sources)=>{
        return buildTargets.map(target=>{
            let {lib,dist,root} = this;
            let [type, format, minified] = target.split("-");
            let inputs = type==='lib'? sources : ['index.js'];
            let srcOpts = {
                es: format==='es',
                single: type==='lib',
                sourcemap: minified ? true : (format==='es'? false : true),
                minified: !!minified
            };
            let outDir = type==='lib'? lib : dist;
    
            return new TargetConfig({
                target,
                sources: inputs.map(input=>{
                    let outName = input.replace(
                        /\.js$/,
                        `.${[
                            type==='dist'? srcOpts.es?'es':'cjs' : null,
                            srcOpts.minified?'min':null,
                            'js'
                        ].filter(x=>x).join('.')}`);
                    if(type==='lib') outName = [srcOpts.es?'es':'cjs',outName].join('/');
                    
                    return new SourceConfig({
                        source: input,
                        config: this.srcCfg(input, outDir.resolve(outName), srcOpts)
                    });
                })
            });
        });
    }
}

/**
 * Configure a source-file for build (single target)
 */
export class SourceConfig{
    /** @param {SourceConfig} c */
    constructor(c){
        this.source = c.source;
        this.config = c.config;
    }
    /** @type {string} */
    source;
    
    /** @type {RollupOptions} */
    config;
}

/**
 * Represent the configuration for a specific build target (e.g. lib-es, dist-cjs, ...)
 */
export class TargetConfig{
    /** @param {TargetConfig} c */
    constructor(c){
        this.target = c.target;
        this.sources = c.sources;
    }
    /** @type {string} */
    target;
    
    /** @type {SourceConfig[]} */
    sources;
}
