/**
 * Frontend Standards Parser - Detect and parse React/Angular/Vue coding standards
 *
 * Detects framework from package.json, parses ESLint plugins, and extracts conventions
 */
import * as fs from 'fs';
import * as path from 'path';
import { promisify } from 'util';
import { consoleLogger } from '../../../utils/logger.js';
const readFile = promisify(fs.readFile);
const stat = promisify(fs.stat);
const FRAMEWORK_DEPENDENCIES = {
    react: ['react', 'react-dom', 'next', 'gatsby', '@remix-run/react', 'preact'],
    angular: ['@angular/core', '@angular/cli'],
    vue: ['vue', 'nuxt', '@nuxt/core', '@vue/cli-service'],
    svelte: ['svelte', '@sveltejs/kit'],
    none: [],
};
const STATE_MANAGEMENT_LIBS = {
    redux: ['react'],
    '@reduxjs/toolkit': ['react'],
    zustand: ['react'],
    jotai: ['react'],
    recoil: ['react'],
    mobx: ['react', 'vue'],
    pinia: ['vue'],
    vuex: ['vue'],
    '@ngrx/store': ['angular'],
    '@ngxs/store': ['angular'],
    'svelte/store': ['svelte'],
};
const CSS_METHODOLOGIES = {
    tailwindcss: 'Tailwind CSS (utility-first)',
    'styled-components': 'styled-components (CSS-in-JS)',
    '@emotion/react': 'Emotion (CSS-in-JS)',
    '@emotion/styled': 'Emotion (CSS-in-JS)',
    'sass': 'Sass/SCSS (preprocessor)',
    'node-sass': 'Sass/SCSS (preprocessor)',
    'less': 'Less (preprocessor)',
    '@vanilla-extract/css': 'Vanilla Extract (zero-runtime CSS-in-TS)',
    'css-modules': 'CSS Modules (scoped CSS)',
};
const TESTING_FRAMEWORKS = {
    jest: 'Jest',
    vitest: 'Vitest',
    '@testing-library/react': 'React Testing Library',
    '@testing-library/vue': 'Vue Testing Library',
    '@testing-library/angular': 'Angular Testing Library',
    cypress: 'Cypress (E2E)',
    playwright: 'Playwright (E2E)',
    '@playwright/test': 'Playwright (E2E)',
};
const BUNDLERS = {
    vite: 'Vite',
    webpack: 'webpack',
    esbuild: 'esbuild',
    rollup: 'Rollup',
    parcel: 'Parcel',
    '@angular/cli': 'Angular CLI (webpack)',
    next: 'Next.js (webpack/turbopack)',
    nuxt: 'Nuxt (Vite/webpack)',
};
export class FrontendStandardsParser {
    constructor(options = {}) {
        this.logger = options.logger ?? consoleLogger;
    }
    async parse(projectPath) {
        const warnings = [];
        const result = {
            framework: 'none',
            testing: { frameworks: [] },
            eslintPlugins: [],
            componentNaming: {
                convention: 'PascalCase',
                fileExtension: '.tsx',
            },
            warnings,
        };
        // Read package.json
        const packageJsonPath = path.join(projectPath, 'package.json');
        if (!(await this.fileExists(packageJsonPath))) {
            warnings.push('No package.json found');
            return result;
        }
        let packageJson;
        try {
            const content = await readFile(packageJsonPath, 'utf-8');
            packageJson = JSON.parse(content);
        }
        catch (error) {
            warnings.push(`Failed to parse package.json: ${error}`);
            return result;
        }
        const allDeps = {
            ...packageJson.dependencies,
            ...packageJson.devDependencies,
        };
        // Detect framework
        const { framework, version } = this.detectFramework(allDeps);
        result.framework = framework;
        result.frameworkVersion = version;
        // Set framework-specific defaults
        this.setFrameworkDefaults(result, framework);
        // Detect state management
        result.stateManagement = this.detectStateManagement(allDeps, framework);
        // Detect CSS methodology
        result.cssMethodology = this.detectCssMethodology(allDeps);
        // Detect testing frameworks
        result.testing = this.detectTesting(allDeps);
        // Detect bundler
        result.bundler = this.detectBundler(allDeps);
        // Parse ESLint config for plugins
        result.eslintPlugins = await this.detectEslintPlugins(projectPath, framework);
        // Detect routing
        result.routing = this.detectRouting(allDeps, framework);
        // Framework-specific config parsing
        if (framework === 'angular') {
            await this.parseAngularConfig(projectPath, result);
        }
        else if (framework === 'vue') {
            await this.parseVueConfig(projectPath, result);
        }
        this.logger.debug(`Detected frontend: ${framework} ${version ?? ''}`);
        return result;
    }
    detectFramework(deps) {
        for (const [framework, markers] of Object.entries(FRAMEWORK_DEPENDENCIES)) {
            if (framework === 'none')
                continue;
            for (const marker of markers) {
                if (deps[marker]) {
                    return {
                        framework: framework,
                        version: deps[marker]?.replace(/[\^~]/, ''),
                    };
                }
            }
        }
        return { framework: 'none' };
    }
    setFrameworkDefaults(result, framework) {
        switch (framework) {
            case 'react':
                result.componentNaming = {
                    convention: 'PascalCase',
                    fileExtension: '.tsx',
                    folderStructure: 'components/{ComponentName}/{ComponentName}.tsx',
                };
                result.hooks = {
                    enabled: true,
                    customHooksPattern: 'use{Name}',
                };
                break;
            case 'angular':
                result.componentNaming = {
                    convention: 'kebab-case',
                    fileExtension: '.ts',
                    folderStructure: '{feature}/{feature}.component.ts',
                };
                break;
            case 'vue':
                result.componentNaming = {
                    convention: 'PascalCase',
                    fileExtension: '.vue',
                    folderStructure: 'components/{ComponentName}.vue',
                };
                break;
            case 'svelte':
                result.componentNaming = {
                    convention: 'PascalCase',
                    fileExtension: '.svelte',
                    folderStructure: 'components/{ComponentName}.svelte',
                };
                break;
        }
    }
    detectStateManagement(deps, framework) {
        for (const [lib, frameworks] of Object.entries(STATE_MANAGEMENT_LIBS)) {
            if (deps[lib] && (frameworks.includes(framework) || framework === 'none')) {
                return {
                    name: lib,
                    version: deps[lib]?.replace(/[\^~]/, ''),
                };
            }
        }
        return undefined;
    }
    detectCssMethodology(deps) {
        for (const [lib, description] of Object.entries(CSS_METHODOLOGIES)) {
            if (deps[lib]) {
                return {
                    name: lib,
                    details: description,
                };
            }
        }
        return undefined;
    }
    detectTesting(deps) {
        const frameworks = [];
        let e2e;
        for (const [lib, name] of Object.entries(TESTING_FRAMEWORKS)) {
            if (deps[lib]) {
                if (name.includes('E2E')) {
                    e2e = name;
                }
                else {
                    frameworks.push(name);
                }
            }
        }
        return {
            frameworks,
            e2e,
            coverage: !!(deps['@vitest/coverage-v8'] || deps['jest'] || deps['c8']),
        };
    }
    detectBundler(deps) {
        for (const [lib, name] of Object.entries(BUNDLERS)) {
            if (deps[lib]) {
                return {
                    name,
                    version: deps[lib]?.replace(/[\^~]/, ''),
                };
            }
        }
        return undefined;
    }
    detectRouting(deps, framework) {
        const routingLibs = {
            'react-router-dom': 'React Router',
            '@tanstack/react-router': 'TanStack Router',
            'vue-router': 'Vue Router',
            '@angular/router': 'Angular Router',
        };
        for (const [lib, name] of Object.entries(routingLibs)) {
            if (deps[lib]) {
                return {
                    library: name,
                    version: deps[lib]?.replace(/[\^~]/, ''),
                };
            }
        }
        // Built-in routing for meta-frameworks
        if (deps['next'])
            return { library: 'Next.js App Router' };
        if (deps['nuxt'])
            return { library: 'Nuxt Router' };
        if (deps['@sveltejs/kit'])
            return { library: 'SvelteKit Router' };
        return undefined;
    }
    async detectEslintPlugins(projectPath, framework) {
        const eslintConfigs = [
            '.eslintrc.json',
            '.eslintrc.js',
            '.eslintrc.cjs',
            '.eslintrc.yml',
            'eslint.config.js',
            'eslint.config.mjs',
        ];
        const plugins = [];
        for (const config of eslintConfigs) {
            const configPath = path.join(projectPath, config);
            if (await this.fileExists(configPath)) {
                try {
                    const content = await readFile(configPath, 'utf-8');
                    // Common framework-specific plugins
                    const frameworkPlugins = {
                        react: ['plugin:react/', 'plugin:react-hooks/', 'plugin:jsx-a11y/'],
                        angular: ['plugin:@angular-eslint/', 'plugin:@typescript-eslint/'],
                        vue: ['plugin:vue/', 'plugin:vue-scoped-css/'],
                        svelte: ['plugin:svelte/'],
                        none: [],
                    };
                    for (const plugin of frameworkPlugins[framework] ?? []) {
                        if (content.includes(plugin)) {
                            plugins.push(plugin.replace('plugin:', '').replace('/', ''));
                        }
                    }
                    // Check for common plugins
                    const commonPlugins = [
                        'import',
                        'prettier',
                        '@typescript-eslint',
                        'testing-library',
                        'jest',
                        'vitest',
                    ];
                    for (const plugin of commonPlugins) {
                        if (content.includes(`"${plugin}"`) || content.includes(`'${plugin}'`)) {
                            plugins.push(plugin);
                        }
                    }
                    break;
                }
                catch {
                    continue;
                }
            }
        }
        return Array.from(new Set(plugins)); // Remove duplicates
    }
    async parseAngularConfig(projectPath, result) {
        const angularJsonPath = path.join(projectPath, 'angular.json');
        if (!(await this.fileExists(angularJsonPath)))
            return;
        try {
            const content = await readFile(angularJsonPath, 'utf-8');
            const config = JSON.parse(content);
            // Extract style preprocessor
            const projects = config.projects ?? {};
            const firstProject = Object.values(projects)[0];
            if (firstProject?.architect?.build?.options?.styles) {
                const styles = firstProject.architect.build.options.styles;
                if (styles.some((s) => s.endsWith('.scss'))) {
                    result.cssMethodology = { name: 'sass', details: 'Sass/SCSS (preprocessor)' };
                }
            }
            this.logger.debug('Parsed angular.json');
        }
        catch {
            result.warnings.push('Failed to parse angular.json');
        }
    }
    async parseVueConfig(projectPath, result) {
        // Check for Vue config files
        const vueConfigs = ['vue.config.js', 'vite.config.ts', 'vite.config.js', 'nuxt.config.ts'];
        for (const config of vueConfigs) {
            const configPath = path.join(projectPath, config);
            if (await this.fileExists(configPath)) {
                try {
                    const content = await readFile(configPath, 'utf-8');
                    // Detect Composition API usage
                    if (content.includes('composition') || content.includes('script setup')) {
                        result.hooks = {
                            enabled: true,
                            customHooksPattern: 'use{Name}',
                        };
                    }
                    this.logger.debug(`Parsed ${config}`);
                }
                catch {
                    continue;
                }
            }
        }
    }
    async fileExists(filePath) {
        try {
            await stat(filePath);
            return true;
        }
        catch {
            return false;
        }
    }
}
/**
 * Convenience function for quick frontend standards parsing
 */
export async function parseFrontendStandards(projectPath, options) {
    const parser = new FrontendStandardsParser(options);
    return parser.parse(projectPath);
}
//# sourceMappingURL=frontend-standards-parser.js.map