/* eslint-env node */
'use strict';

const fg = require('fast-glob');
const fs = require('fs');
const path = require('path');
const recast = require('recast');
const babelParser = require('recast/parsers/babel');
const builders = recast.types.builders;
const chokidar = require('chokidar');

module.exports = {
    name: require('./package').name,

    getGeneratedFileHeader() {
        const year = new Date().getFullYear();
        return `/**
 * ███████╗██╗     ███████╗███████╗████████╗██████╗  █████╗ ███████╗███████╗
 * ██╔════╝██║     ██╔════╝██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██╔════╝
 * █████╗  ██║     █████╗  █████╗     ██║   ██████╔╝███████║███████╗█████╗  
 * ██╔══╝  ██║     ██╔══╝  ██╔══╝     ██║   ██╔══██╗██╔══██║╚════██║██╔══╝  
 * ██║     ███████╗███████╗███████╗   ██║   ██████╔╝██║  ██║███████║███████╗
 * ╚═╝     ╚══════╝╚══════╝╚══════╝   ╚═╝   ╚═════╝ ╚═╝  ╚═╝╚══════╝╚══════╝
 * 
 * AUTO-GENERATED FILE - DO NOT EDIT
 * 
 * This file is automatically generated by the Fleetbase extension build system.
 * Any manual changes will be overwritten on the next build.
 * 
 * @generated
 * @copyright © ${year} Fleetbase Pte Ltd. All rights reserved.
 * @license AGPL-3.0-or-later
 */

`;
    },

    included(app) {
        this._super.included.apply(this, arguments);

        console.log('\n' + '/'.repeat(70));
        console.log('[Fleetbase] Extension Build System');
        console.log('/'.repeat(70));

        // Generate files on startup
        this.generateExtensionFiles();

        // Watch for changes in development
        this.watchExtensionFiles();
    },

    async generateExtensionFiles() {
        // Clean up old/stale extensions directory before generating new files
        const extensionsDir = path.join(this.project.root, 'app', 'extensions');
        if (fs.existsSync(extensionsDir)) {
            console.log('[Fleetbase] Cleaning up old extensions directory...');
            fs.rmSync(extensionsDir, { recursive: true, force: true });
        }

        const extensions = await this.getExtensions();

        if (extensions.length === 0) {
            console.log('[Fleetbase] No extensions found');
            return;
        }

        console.log(`[Fleetbase] Discovered ${extensions.length} extension(s)`);
        extensions.forEach((ext) => {
            console.log(`[Fleetbase]   - ${ext.name} (v${ext.version})`);
        });
        console.log('');

        // Generate extension shims
        this.generateExtensionShims(extensions);

        // Generate extension loaders
        this.generateExtensionLoaders(extensions);

        // Generate router
        this.generateRouter(extensions);

        // Generate manifest
        this.generateExtensionsManifest(extensions);
    },

    getExtensions() {
        return new Promise((resolve, reject) => {
            const extensions = [];
            const seenPackages = new Set();
            const cwd = this.project.root;

            return fg(['node_modules/*/package.json', 'node_modules/*/*/package.json'], { cwd })
                .then((results) => {
                    for (let i = 0; i < results.length; i++) {
                        const packagePath = path.join(cwd, results[i]);
                        const packageJson = fs.readFileSync(packagePath);
                        let packageData = null;

                        try {
                            packageData = JSON.parse(packageJson);
                        } catch (e) {
                            console.warn(`Could not parse package.json at ${packagePath}:`, e);
                            continue;
                        }

                        if (!packageData || !packageData.keywords || !packageData.keywords.includes('fleetbase-extension') || !packageData.keywords.includes('ember-engine')) {
                            continue;
                        }

                        // If we've seen this package before, skip it
                        if (seenPackages.has(packageData.name)) {
                            continue;
                        }

                        seenPackages.add(packageData.name);
                        extensions.push(this.only(packageData, ['name', 'description', 'version', 'fleetbase', 'keywords', 'license', 'repository']));
                    }

                    resolve(extensions);
                })
                .catch(reject);
        });
    },

    only(subject, props = []) {
        const keys = Object.keys(subject);
        const result = {};

        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];

            if (props.includes(key)) {
                result[key] = subject[key];
            }
        }

        return result;
    },

    getExtensionMountPath(extensionName) {
        let extensionNameSegments = extensionName.split('/');
        let mountName = extensionNameSegments[1];

        if (typeof mountName !== 'string') {
            mountName = extensionNameSegments[0];
        }

        return mountName.replace('-engine', '');
    },

    generateExtensionShims(extensions) {
        const extensionsDir = path.join(this.project.root, 'app', 'extensions');

        if (!fs.existsSync(extensionsDir)) {
            fs.mkdirSync(extensionsDir, { recursive: true });
        }

        extensions.forEach((extension) => {
            const extensionPath = path.join(this.project.root, 'node_modules', extension.name, 'addon', 'extension.js');

            if (!fs.existsSync(extensionPath)) {
                return;
            }

            const extensionContent = fs.readFileSync(extensionPath, 'utf8');
            const mountPath = extension.fleetbase?.route || this.getExtensionMountPath(extension.name);
            const shimFile = path.join(extensionsDir, `${mountPath}.js`);
            const fileContent = this.getGeneratedFileHeader() + extensionContent;

            fs.writeFileSync(shimFile, fileContent, 'utf8');
            console.log(`[Fleetbase]   ✓ Generated app/extensions/${mountPath}.js`);
        });
    },

    generateExtensionLoaders(extensions) {
        const extensionsDir = path.join(this.project.root, 'app', 'extensions');

        if (!fs.existsSync(extensionsDir)) {
            fs.mkdirSync(extensionsDir, { recursive: true });
        }

        const imports = [];
        const loaders = {};

        extensions.forEach((extension) => {
            const extensionPath = path.join(this.project.root, 'node_modules', extension.name, 'addon', 'extension.js');

            if (!fs.existsSync(extensionPath)) {
                return;
            }

            const mountPath = extension.fleetbase?.route || this.getExtensionMountPath(extension.name);
            const camelCaseName = mountPath.replace(/-([a-z])/g, (g) => g[1].toUpperCase());

            imports.push(`import ${camelCaseName} from './${mountPath}';`);
            loaders[extension.name] = `() => ${camelCaseName}`;
        });

        const loadersContent =
            this.getGeneratedFileHeader() +
            `${imports.join('\n')}

export const EXTENSION_LOADERS = {
${Object.entries(loaders)
    .map(([name, loader]) => `    '${name}': ${loader}`)
    .join(',\n')}
};

export const getExtensionLoader = (packageName) => {
    return EXTENSION_LOADERS[packageName];
};

export default getExtensionLoader;
`;

        const loadersFile = path.join(extensionsDir, 'index.js');
        fs.writeFileSync(loadersFile, loadersContent, 'utf8');
        console.log(`[Fleetbase]   ✓ Generated app/extensions/index.js`);
    },

    generateRouter(extensions) {
        const consoleExtensions = extensions.filter((extension) => !extension.fleetbase || extension.fleetbase.mount !== 'root');
        const rootExtensions = extensions.filter((extension) => extension.fleetbase && extension.fleetbase.mount === 'root');
        const routerMapPath = path.join(this.project.root, 'router.map.js');
        const routerFileContents = fs.readFileSync(routerMapPath, 'utf-8');
        const ast = recast.parse(routerFileContents, { parser: babelParser });

        recast.visit(ast, {
            visitCallExpression(path) {
                if (path.value.type === 'CallExpression' && path.value.callee.property.name === 'route' && path.value.arguments[0].value === 'console') {
                    let functionExpression;

                    // Find the function expression
                    path.value.arguments.forEach((arg) => {
                        if (arg.type === 'FunctionExpression') {
                            functionExpression = arg;
                        }
                    });

                    if (functionExpression) {
                        // Check and add the new engine mounts
                        consoleExtensions.forEach((extension) => {
                            const mountPath = module.exports.getExtensionMountPath(extension.name);
                            let route = mountPath;

                            if (extension.fleetbase && extension.fleetbase.route) {
                                route = extension.fleetbase.route;
                            }

                            // Check if engine is already mounted
                            const isMounted = functionExpression.body.body.some((expressionStatement) => {
                                return expressionStatement.expression.arguments[0].value === extension.name;
                            });

                            // If not mounted, append to the function body
                            if (!isMounted) {
                                functionExpression.body.body.push(
                                    builders.expressionStatement(
                                        builders.callExpression(builders.memberExpression(builders.thisExpression(), builders.identifier('mount')), [
                                            builders.literal(extension.name),
                                            builders.objectExpression([
                                                builders.property('init', builders.identifier('as'), builders.literal(route)),
                                                builders.property('init', builders.identifier('path'), builders.literal(route)),
                                            ]),
                                        ])
                                    )
                                );
                            }
                        });
                    }
                }

                if (path.value.type === 'CallExpression' && path.value.callee.property.name === 'map') {
                    let functionExpression;

                    path.value.arguments.forEach((arg) => {
                        if (arg.type === 'FunctionExpression') {
                            functionExpression = arg;
                        }
                    });

                    if (functionExpression) {
                        rootExtensions.forEach((extension) => {
                            const mountPath = module.exports.getExtensionMountPath(extension.name);
                            let route = mountPath;

                            if (extension.fleetbase && extension.fleetbase.route) {
                                route = extension.fleetbase.route;
                            }

                            const isMounted = functionExpression.body.body.some((expressionStatement) => {
                                return expressionStatement.expression.arguments[0].value === extension.name;
                            });

                            if (!isMounted) {
                                functionExpression.body.body.push(
                                    builders.expressionStatement(
                                        builders.callExpression(builders.memberExpression(builders.thisExpression(), builders.identifier('mount')), [
                                            builders.literal(extension.name),
                                            builders.objectExpression([
                                                builders.property('init', builders.identifier('as'), builders.literal(route)),
                                                builders.property('init', builders.identifier('path'), builders.literal(route)),
                                            ]),
                                        ])
                                    )
                                );
                            }
                        });
                    }
                }

                this.traverse(path);
            },
        });

        const output = recast.print(ast, { quote: 'single' }).code;
        const routerFile = path.join(this.project.root, 'app/router.js');
        const fileContent = this.getGeneratedFileHeader() + output;
        fs.writeFileSync(routerFile, fileContent);
        console.log(`[Fleetbase]   ✓ Generated app/router.js`);
    },

    generateExtensionsManifest(extensions) {
        const publicDir = path.join(this.project.root, 'public');

        if (!fs.existsSync(publicDir)) {
            fs.mkdirSync(publicDir, { recursive: true });
        }

        const manifest = extensions.map((ext) => ({
            name: ext.name,
            version: ext.version,
            route: ext.fleetbase?.route,
        }));

        const manifestFile = path.join(publicDir, 'extensions.json');
        fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 2), 'utf8');
        console.log(`[Fleetbase]   ✓ Generated public/extensions.json`);
    },

    watchExtensionFiles() {
        const isDevelopment = process.env.EMBER_ENV !== 'production';

        if (!isDevelopment) {
            return;
        }

        const self = this;

        this.getExtensions().then((extensions) => {
            const extensionFiles = [];

            extensions.forEach((extension) => {
                const extensionPath = path.join(self.project.root, 'node_modules', extension.name, 'addon', 'extension.js');
                if (fs.existsSync(extensionPath)) {
                    extensionFiles.push(extensionPath);
                }
            });

            if (extensionFiles.length === 0) {
                return;
            }

            const watcher = chokidar.watch(extensionFiles, {
                persistent: true,
                ignoreInitial: true,
            });

            watcher.on('change', (filePath) => {
                console.log(`\n[Fleetbase] Extension file changed: ${path.basename(filePath)}`);
                console.log('[Fleetbase] Regenerating extension files...\n');
                self.generateExtensionFiles();
            });
        });
    },
};
