import type { Component, DefineComponent } from 'vue';

export type VicemModule = { id: string; path: string };

export type VicemThemes = { client: string | null; admin: string | null };

export type VicemLayouts = {
    client: Record<string, string>;
    admin: Record<string, string>;
};

declare global {
    interface Window {
        __VICEM_THEMES__?: VicemThemes;
        __VICEM_MODULES__?: VicemModule[];
        __VICEM_LAYOUTS__?: VicemLayouts;
    }
}

export const pageLoaders = import.meta.glob<{ default: DefineComponent }>([
    '../pages/**/*.vue',
    '../../../themes/*/resources/js/pages/**/*.vue',
    '../../../themes/*/resources/js/admin/**/*.vue',
    '../../../themes/*/overrides/**/*.vue',
    '../../../modules/*/*/resources/js/**/*.vue',
]);

export type ModuleName = {
    id: string;
    vendor: string;
    package: string;
    zone: string;
    page: string;
};

export function activeThemes(): VicemThemes {
    return (
        window.__VICEM_THEMES__ ?? {
            client: null,
            admin: null,
        }
    );
}

export function frontendModules(): VicemModule[] {
    return window.__VICEM_MODULES__ ?? [];
}

export function layoutMap(): VicemLayouts {
    return (
        window.__VICEM_LAYOUTS__ ?? {
            client: {},
            admin: {},
        }
    );
}

export function parseModuleName(name: string): ModuleName | null {
    const match = name.match(/^module\/([^/]+)\/([^/]+)\/([^/]+)\/(.+)$/);

    if (!match) {
        return null;
    }

    return {
        id: `${match[1]}/${match[2]}`,
        vendor: match[1],
        package: match[2],
        zone: match[3],
        page: `${match[3]}/${match[4]}`,
    };
}

export function moduleDiskPath(id: string): string | null {
    return frontendModules().find((entry) => entry.id === id)?.path ?? null;
}

const MODULE_DIR_TYPES = [
    'addons',
    'servers',
    'gateways',
    'registrars',
    'notifications',
    'mail',
    'reports',
    'fraud',
] as const;

type PageLoader = () => Promise<{ default: DefineComponent }>;

function pageLoaderMap(): Record<string, PageLoader> {
    return pageLoaders as Record<string, PageLoader>;
}

/**
 * Clé Vite (`import.meta.glob`) : relative au fichier, parfois absolue
 * après bundling. On compare aussi la queue `modules/…` / `themes/…`.
 */
export function globTail(path: string): string {
    for (const folder of [
        '/modules/',
        '/themes/',
        '/resources/js/pages/',
    ] as const) {
        const index = path.indexOf(folder);

        if (index !== -1) {
            return path.slice(index + 1);
        }
    }

    return path.replace(/^(\.\.\/)+/, '');
}

export function matchPageLoader(candidates: string[]): PageLoader | null {
    const loaders = pageLoaderMap();
    const keys = Object.keys(loaders);

    for (const candidate of candidates) {
        if (loaders[candidate]) {
            return loaders[candidate];
        }
    }

    for (const candidate of candidates) {
        const tail = globTail(candidate);
        const found = keys.find(
            (key) =>
                key === candidate ||
                key.endsWith(candidate) ||
                key.endsWith(tail) ||
                key.endsWith('/' + tail),
        );

        if (found) {
            return loaders[found];
        }
    }

    return null;
}

export function matchModulePageLoader(module: ModuleName): PageLoader | null {
    const endings = [
        `/${module.package}/resources/js/pages/${module.page}.vue`,
    ];

    if (module.zone === 'client') {
        endings.push(
            `/${module.package}/resources/js/pages/public/${module.page.slice('client/'.length)}.vue`,
        );
    }

    const themes = activeThemes();
    const slug = module.zone === 'admin' ? themes.admin : themes.client;

    if (slug) {
        endings.unshift(
            `/themes/${slug}/overrides/${module.vendor}/${module.package}/pages/${module.page}.vue`,
        );
    }

    const loaders = pageLoaderMap();

    for (const [key, loader] of Object.entries(loaders)) {
        if (endings.some((ending) => key.endsWith(ending))) {
            return loader;
        }
    }

    return null;
}

export function moduleViewCandidates(
    moduleId: string,
    rel: string,
    area: 'admin' | 'client',
): string[] {
    const clean = rel.replace(/\.vue$/, '').replace(/^\/+/, '');
    const parts = moduleId.split('/');
    const themes = activeThemes();
    const slug = area === 'admin' ? themes.admin : themes.client;
    const candidates: string[] = [];

    if (parts.length === 2 && slug) {
        candidates.push(
            `../../../themes/${slug}/overrides/${parts[0]}/${parts[1]}/${clean}.vue`,
        );
    }

    const disk = moduleDiskPath(moduleId);

    if (disk && !disk.startsWith('/') && !/^[A-Za-z]:[\\/]/.test(disk)) {
        candidates.push(`../../../${disk}/resources/js/${clean}.vue`);
    }

    const packageName = parts[1];

    if (packageName && clean.startsWith('pages/')) {
        for (const type of MODULE_DIR_TYPES) {
            candidates.push(
                `../../../modules/${type}/${packageName}/resources/js/${clean}.vue`,
            );
        }
    }

    return candidates;
}

export function pageCandidates(name: string): string[] {
    const themes = activeThemes();
    const candidates: string[] = [];
    const module = parseModuleName(name);

    if (module) {
        const area = module.zone === 'admin' ? 'admin' : 'client';
        candidates.push(
            ...moduleViewCandidates(module.id, `pages/${module.page}`, area),
        );

        if (module.zone === 'client') {
            candidates.push(
                ...moduleViewCandidates(
                    module.id,
                    `pages/public/${module.page.slice('client/'.length)}`,
                    'client',
                ),
            );
        }

        candidates.push(`../pages/${name}.vue`);

        return candidates;
    }

    const themeAdmin = name.match(/^theme\/([^/]+)\/(.+)$/);

    if (themeAdmin) {
        candidates.push(
            `../../../themes/${themeAdmin[1]}/resources/js/admin/${themeAdmin[2]}.vue`,
        );
    } else if (
        name.startsWith('client/help/') ||
        name.startsWith('client/status/')
    ) {
        if (themes.client) {
            candidates.push(
                `../../../themes/${themes.client}/resources/js/pages/${name.slice('client/'.length)}.vue`,
            );
        }
    } else if (
        name.startsWith('client/') ||
        name.startsWith('order/') ||
        name.startsWith('help/') ||
        name.startsWith('status/') ||
        name === 'auth/ClientLogin' ||
        name === 'auth/ClientRegister' ||
        name === 'auth/ClientTwoFactorChallenge'
    ) {
        if (themes.client) {
            candidates.push(
                `../../../themes/${themes.client}/resources/js/pages/${name}.vue`,
            );
        }
    } else if (
        name.startsWith('admin/') ||
        name.startsWith('settings/') ||
        name.startsWith('auth/') ||
        name === 'Dashboard'
    ) {
        if (themes.admin) {
            candidates.push(
                `../../../themes/${themes.admin}/resources/js/pages/${name}.vue`,
            );
        }
    }

    candidates.push(`../pages/${name}.vue`);

    return candidates;
}

export async function resolvePage(name: string): Promise<DefineComponent> {
    const module = parseModuleName(name);
    const loader =
        matchPageLoader(pageCandidates(name)) ??
        (module ? matchModulePageLoader(module) : null);

    if (!loader) {
        throw new Error(`Page not found: ${name}`);
    }

    const mod = await loader();

    return mod.default;
}

export function resolveModuleLoader(
    moduleId: string,
    rel: string,
    area: 'admin' | 'client',
): (() => Promise<{ default: Component }>) | null {
    return matchPageLoader(moduleViewCandidates(moduleId, rel, area));
}

export function hostLayoutId(name: string): string | null {
    if (name === 'Welcome') {
        return null;
    }

    const module = parseModuleName(name);

    if (module) {
        switch (module.zone) {
            case 'admin':
                return 'admin';
            case 'client':
                return 'client';
            case 'order':
                return 'client.order';
            case 'guest':
                return 'client.guest';
            case 'embed':
                return null;
            case 'public':
                return `client.public.${module.package}`;
            default:
                return 'admin';
        }
    }

    if (
        name === 'auth/ClientLogin' ||
        name === 'auth/ClientRegister' ||
        name === 'auth/ClientTwoFactorChallenge' ||
        name === 'client/OauthConsent'
    ) {
        return 'client.guest';
    }

    if (name.startsWith('auth/')) {
        return 'admin.auth';
    }

    if (name.startsWith('settings/')) {
        return 'admin.settings';
    }

    if (name.startsWith('order/')) {
        return 'client.order';
    }

    if (name.startsWith('help/')) {
        return 'client.public.knowledgebase';
    }

    if (name === 'status/Embed') {
        return null;
    }

    if (name.startsWith('status/')) {
        return 'client.public.network-status';
    }

    if (name.startsWith('client/')) {
        return 'client';
    }

    if (name.startsWith('theme/')) {
        return 'admin.theme';
    }

    if (name.startsWith('admin/')) {
        return 'admin';
    }

    return 'admin';
}

export function layoutFileFor(id: string | null): string | null {
    if (id === null) {
        return null;
    }

    const maps = layoutMap();
    const area = id.startsWith('admin') ? 'admin' : 'client';
    const mapped = maps[area]?.[id];

    if (mapped) {
        return mapped;
    }

    if (id.startsWith('client.public')) {
        return maps.client?.['client.public'] ?? 'ClientPublicLayout';
    }

    const defaults: Record<string, string> = {
        client: 'ClientLayout',
        'client.guest': 'ClientGuestLayout',
        'client.order': 'ClientOrderLayout',
        'client.public': 'ClientPublicLayout',
        admin: 'AppLayout',
        'admin.auth': 'AuthLayout',
        'admin.settings': 'settings/Layout',
        'admin.theme': 'AppLayout',
    };

    return defaults[id] ?? null;
}

export function layoutNeedsImpersonation(id: string | null): boolean {
    if (id === null || id.startsWith('admin') || id === 'client.guest') {
        return false;
    }

    return (
        id === 'client' ||
        id === 'client.order' ||
        id.startsWith('client.public')
    );
}
