import type { ApplicationService } from '@adonisjs/core/types';
import { TagContract } from 'edge.js/types';
import router from '@adonisjs/core/services/router';
import type { Edge } from 'edge.js';
import type { HttpRouterService } from '@adonisjs/core/types';
import StardustMiddleware from '#middleware/stardust_middleware';

export default class StardustProvider {
    public static needsApplication: boolean = true;
    protected app: ApplicationService;

    constructor(app: ApplicationService) {
        this.app = app;
    }

    // https://edgejs.dev/docs/creating-custom-tags
    private stardustTag: TagContract = {
        block: false,
        seekable: true,
        tagName: 'routes',
        compile(_, buffer, token) {
            // buffer.outputVariableName = 'out';
            // buffer.outputRaw('Hello from router tag');

            // const expression = parser.utils.transformAst(
            //     parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename),
            //     token.filename,
            //     parser,
            // );
            // const outputExpression = `${parser.utils.stringify(expression)}.split("").reverse().join("")`;
            //  ''test'.split("").reverse().join("")'

            // console.log(JSON.stringify(expression, null, 2));
            buffer.writeExpression(`\n out += state.routes(state.cspNonce)`, token.filename, token.loc.start.line);
        },
    };

    /**
     * Register the `@routes()` tag
     */
    private async registerStardustTag(edge: Edge) {
        if (!this.app.usingEdgeJS) return;
        // const { edgePluginInertia } = await import('../src/plugins/edge/plugin.js');
        // edgeExports.default.use(edgePluginInertia());
        edge.registerTag(this.stardustTag);
        // edgeExports.registerTag(this.inertiaTag).
    }

    private registerRoutesGlobal(edge: Edge, namedRoutes: Record<string, string>) {
        // Registering a global function
        edge.global('routes', (cspNonce: string | undefined) => {
            return `
                <script${cspNonce ? ` nonce="${cspNonce}"` : ''}>
                    (globalThis || window).stardust = { path: ${JSON.stringify(globalThis.stardust.pathname)}, namedRoutes: ${JSON.stringify(namedRoutes)} };
                </script>
            `;
        });
        // edge.global('reverse', 'arno');
    }

    /**
     * Returns list of named routes
     */
    private getNamedRoutes(router: HttpRouterService) {
        /**
         * Only sharing the main domain routes. Subdomains are
         * ignored for now. Let's see if many people need it
         */

        const mainDomainRoutes = router.toJSON()?.['root'] ?? [];
        return mainDomainRoutes.reduce((routes: any, route) => {
            if (route.name) {
                routes[route.name] = route.pattern;
            } else if (typeof route.handler === 'string') {
                routes[route.handler] = route.pattern;
            }
            return routes;
        }, {});
    }

    // private registerRoutesGlobal;
    /**
     * Registers named routes on the global scope in order to seamlessly support
     * stardust's functionality on the server
     * @param namedRoutes
     */
    // private registerSsrRoutes(namedRoutes) {
    //     globalThis.stardust = { namedRoutes };
    // }

    public async ready(): Promise<void> {
        // this.app.container.bind('EidelLev/Stardust/Middleware', () => Stardust_1.default);
        this.app.container.bind(StardustMiddleware, () => {
            // return new InertiaMiddleware(config, vite);
            return new StardustMiddleware();
        });
        // this.app.container.(['edge.js', 'Adonis/Core/Route'], (View, Route) => {
        //     const namedRoutes = this.getNamedRoutes(Route);
        //     // this.registerRoutesGlobal(View, namedRoutes);
        //     this.registerStardustTag(View);
        //     // this.registerSsrRoutes(namedRoutes);
        // });
        const { default: edge } = await import('edge.js');
        // const router = await this.app.container.make('router');
        // this.app.container.resolving('router', async (router) => {
        // const routerService = await resolver.make('router')
        const namedRoutes = this.getNamedRoutes(router);
        this.registerRoutesGlobal(edge, namedRoutes);
        await this.registerStardustTag(edge);
        // });
    }
}
