import { defineConfig } from "vitest/config";
import { VitestPackageInstaller } from "vitest/node";
import path from "path";
import { fileURLToPath } from "url";
import { createRequire } from "module";
const vitest_config_filename = fileURLToPath(import.meta.url);
const vitest_config_dirname = path.dirname(vitest_config_filename);
const vitest_config_require = createRequire(import.meta.url);
async function ensurePackagesInstalled() {
    const installer = new VitestPackageInstaller();
    const installed = await installer.ensureInstalled('jsdom', process.cwd());
    if (!installed) {
        console.log('ReactLynx Testing Library requires jsdom to be installed.');
        process.exit(1);
    }
}
const createVitestConfig = async (options)=>{
    await ensurePackagesInstalled();
    const runtimeOSSPkgName = '@lynx-js/react';
    const runtimePkgName = options?.runtimePkgName ?? runtimeOSSPkgName;
    const runtimeDir = path.dirname(vitest_config_require.resolve(`${runtimePkgName}/package.json`));
    const runtimeOSSDir = path.dirname(vitest_config_require.resolve(`${runtimeOSSPkgName}/package.json`, {
        paths: [
            runtimeDir,
            vitest_config_dirname
        ]
    }));
    const preactDir = path.dirname(vitest_config_require.resolve('preact/package.json', {
        paths: [
            runtimeOSSDir
        ]
    }));
    const generateAlias = (pkgName, pkgDir, resolveDir)=>{
        const pkgExports = vitest_config_require(path.join(pkgDir, 'package.json')).exports;
        const pkgAlias = [];
        Object.keys(pkgExports).forEach((key)=>{
            const name = path.posix.join(pkgName, key);
            pkgAlias.push({
                find: new RegExp('^' + name + '$'),
                replacement: vitest_config_require.resolve(name, {
                    paths: [
                        resolveDir,
                        vitest_config_dirname
                    ]
                })
            });
        });
        return pkgAlias;
    };
    const runtimeOSSAlias = generateAlias(runtimeOSSPkgName, runtimeOSSDir, runtimeDir);
    let runtimeAlias = [];
    if (runtimePkgName !== runtimeOSSPkgName) runtimeAlias = generateAlias(runtimePkgName, runtimeDir, vitest_config_dirname);
    const preactAlias = generateAlias('preact', preactDir, runtimeOSSDir);
    preactAlias.forEach((alias)=>{
        alias.replacement = alias.replacement.replace(/\.js$/, '.mjs');
    });
    const reactAlias = [
        {
            find: /^react$/,
            replacement: vitest_config_require.resolve(runtimeOSSPkgName, {
                paths: [
                    runtimeDir
                ]
            })
        },
        {
            find: /^react\/jsx-runtime$/,
            replacement: vitest_config_require.resolve(path.posix.join(runtimeOSSPkgName, 'jsx-runtime'), {
                paths: [
                    runtimeDir
                ]
            })
        },
        {
            find: /^react\/jsx-dev-runtime$/,
            replacement: vitest_config_require.resolve(path.posix.join(runtimeOSSPkgName, 'jsx-dev-runtime'), {
                paths: [
                    runtimeDir
                ]
            })
        }
    ];
    function transformReactCompilerPlugin() {
        let rootContext, compilerDeps, babel;
        function resolveCompilerDeps(rootContext) {
            const missingBabelPackages = [];
            const [babelPath, babelPluginReactCompilerPath, babelPluginSyntaxJsxPath, babelPluginSyntaxTypescriptPath] = [
                '@babel/core',
                'babel-plugin-react-compiler',
                '@babel/plugin-syntax-jsx',
                "@babel/plugin-syntax-typescript"
            ].map((name)=>{
                try {
                    return vitest_config_require.resolve(name, {
                        paths: [
                            rootContext
                        ]
                    });
                } catch  {
                    missingBabelPackages.push(name);
                }
                return '';
            });
            if (missingBabelPackages.length > 0) throw new Error(`With \`experimental_enableReactCompiler\` enabled, you need to install \`${missingBabelPackages.join('`, `')}\` in your project root to use React Compiler.`);
            return {
                babelPath,
                babelPluginReactCompilerPath,
                babelPluginSyntaxJsxPath,
                babelPluginSyntaxTypescriptPath
            };
        }
        return {
            name: 'transformReactCompilerPlugin',
            enforce: 'pre',
            config (config) {
                rootContext = config.root;
                const reactCompilerRuntimeAlias = [];
                try {
                    reactCompilerRuntimeAlias.push({
                        find: /^react-compiler-runtime$/,
                        replacement: path.join(path.dirname(vitest_config_require.resolve('react-compiler-runtime/package.json', {
                            paths: [
                                rootContext
                            ]
                        })), 'src', 'index.ts')
                    });
                } catch (e) {}
                config.test.alias.push(...reactCompilerRuntimeAlias);
                compilerDeps = resolveCompilerDeps(rootContext);
                const { babelPath } = compilerDeps;
                babel = vitest_config_require(babelPath);
            },
            async transform (sourceText, sourcePath) {
                if (/\.(?:jsx|tsx)$/.test(sourcePath)) {
                    const { babelPluginReactCompilerPath, babelPluginSyntaxJsxPath, babelPluginSyntaxTypescriptPath } = compilerDeps;
                    const isTSX = sourcePath.endsWith('.tsx');
                    try {
                        const result = babel.transformSync(sourceText, {
                            plugins: [
                                [
                                    babelPluginReactCompilerPath,
                                    {
                                        target: '17'
                                    }
                                ],
                                babelPluginSyntaxJsxPath,
                                isTSX ? [
                                    babelPluginSyntaxTypescriptPath,
                                    {
                                        isTSX: true
                                    }
                                ] : null
                            ].filter(Boolean),
                            filename: sourcePath,
                            ast: false,
                            sourceMaps: true
                        });
                        if (result?.code != null && result?.map != null) return {
                            code: result.code,
                            map: result.map
                        };
                        this.error(`babel-plugin-react-compiler transform failed for ${sourcePath}: ${result ? 'missing code or map' : 'no result'}`);
                    } catch (e) {
                        this.error(e);
                    }
                }
                return null;
            }
        };
    }
    function transformReactLynxPlugin() {
        return {
            name: 'transformReactLynxPlugin',
            enforce: 'pre',
            transform (sourceText, sourcePath) {
                const id = sourcePath;
                const regex = /\.(?:js|jsx|mjs|cjs|ts|tsx|mts|cts)(\?.*)?$/;
                if (!regex.test(id)) return null;
                const { transformReactLynxSync } = vitest_config_require('@lynx-js/react/transform');
                const relativePath = normalizeSlashes(path.relative(vitest_config_dirname, sourcePath));
                const basename = path.basename(sourcePath);
                const result = transformReactLynxSync(sourceText, {
                    mode: 'test',
                    pluginName: '',
                    filename: basename,
                    sourcemap: true,
                    snapshot: {
                        preserveJsx: false,
                        runtimePkg: `${runtimePkgName}/internal`,
                        jsxImportSource: runtimePkgName,
                        filename: relativePath,
                        target: 'MIXED'
                    },
                    engineVersion: options?.engineVersion ?? '',
                    dynamicImport: {
                        injectLazyBundle: false,
                        layer: 'test',
                        runtimePkg: `${runtimePkgName}/internal`
                    },
                    directiveDCE: false,
                    defineDCE: false,
                    shake: false,
                    compat: false,
                    worklet: {
                        filename: relativePath,
                        runtimePkg: `${runtimePkgName}/internal`,
                        target: 'MIXED'
                    },
                    refresh: false,
                    cssScope: false
                });
                if (result.errors.length > 0) result.errors.forEach((error)=>{
                    this.error(error.text, error.location);
                });
                if (result.warnings.length > 0) result.warnings.forEach((warning)=>{
                    this.warn(warning.text, warning.location);
                });
                return {
                    code: result.code,
                    map: result.map
                };
            }
        };
    }
    return defineConfig({
        server: {
            fs: {
                allow: [
                    path.join(vitest_config_dirname, '..')
                ]
            }
        },
        plugins: [
            ...options?.experimental_enableReactCompiler ? [
                transformReactCompilerPlugin()
            ] : [],
            transformReactLynxPlugin()
        ],
        test: {
            environment: vitest_config_require.resolve('./env/vitest'),
            globals: true,
            setupFiles: [
                path.join(vitest_config_dirname, 'vitest-global-setup')
            ],
            alias: [
                ...runtimeOSSAlias,
                ...runtimeAlias,
                ...preactAlias,
                ...reactAlias
            ],
            include: options?.include ?? [
                'src/**/*.test.{js,jsx,ts,tsx}'
            ]
        }
    });
};
function normalizeSlashes(file) {
    return file.replaceAll(path.win32.sep, '/');
}
export { createVitestConfig };
