/**
 * @fileoverview OrdoJS CLI - Build command
 */

import {
  CodeSplitter,
  FileSystemRouter,
  OrdoJSCSSInJSCompiler,
  OrdoJSCSSOptimizer,
  OrdoJSCompiler,
  OrdoJSLexer,
  OrdoJSParser,
  OrdoJSSSR
} from '@ordojs/core';
import { Command } from 'commander';
import path from 'path';
import { AssetOptimizer } from '../utils/asset-optimizer.js';
import { DeploymentAdapterManager } from '../utils/deployment/adapter-manager.js';
import { AWSLambdaAdapter, NetlifyAdapter, VercelAdapter } from '../utils/deployment/index.js';
import { copyFile, mkdir, readFile, stat, writeFile } from '../utils/fs.js';
import { CLIError, ErrorType, logger } from '../utils/index.js';

/**
 * Register the build command
 */
export function registerBuildCommand(program: Command): void {
  program
    .command('build')
    .description('Build an OrdoJS component or application')
    .argument('<file>', 'Path to the OrdoJS file (.atom)')
    .option('-o, --out <dir>', 'Output directory', 'dist')
    .option('-m, --minify', 'Minify output', false)
    .option('--no-sourcemap', 'Disable source maps')
    .option('-t, --target <target>', 'Compilation target', 'es2022')
    .option('--static', 'Generate static site (SSG mode)')
    .option('--routes <dir>', 'Routes directory for SSG', 'src/routes')
    .option('--public <dir>', 'Public assets directory', 'public')
    .option('--production', 'Enable production mode with all optimizations', false)
    .option('--analyze', 'Generate bundle analysis report', false)
    .option('--brotli', 'Generate Brotli compressed assets', false)
    .option('--gzip', 'Generate Gzip compressed assets', false)
    .option('--no-optimization', 'Disable all optimizations')
    .option('--bundle-report', 'Generate bundle size report', false)
    .option('--deploy <adapter>', 'Prepare deployment for specified adapter (vercel, netlify, aws-lambda)')
    .action(async (file, options) => {
      try {
        // If production mode is enabled, set all optimization flags
        if (options.production) {
          options.minify = true;
          options.brotli = true;
          options.gzip = true;
          options.analyze = true;
          options.bundleReport = true;
          options.optimization = true;
        }

        if (options.static) {
          await staticBuildCommand(file, options);
        } else {
          await buildCommand(file, options);
        }
      } catch (error) {
        if (error instanceof CLIError) {
          logger.error(`${error.type.toUpperCase()} ERROR: ${error.message}`);
          if (error.code) {
            logger.error(`Error code: ${error.code}`);
          }
          if (error.suggestions && error.suggestions.length > 0) {
            logger.info('Suggestions:');
            error.suggestions.forEach(suggestion => {
              logger.info(`  - ${suggestion}`);
            });
          }
        } else {
          logger.error(`Build failed: ${error instanceof Error ? error.message : String(error)}`);
        }
        process.exit(1);
      }
    });
}

/**
 * Build command implementation
 */
export async function buildCommand(
  file: string,
  options: {
    out: string;
    minify: boolean;
    sourcemap: boolean;
    target: string;
    brotli?: boolean;
    gzip?: boolean;
    analyze?: boolean;
    bundleReport?: boolean;
    optimization?: boolean;
    production?: boolean;
    deploy?: string;
  }
): Promise<void> {
  logger.info(`Building ${file}...`);

  try {
    // Validate file extension
    if (!file.endsWith('.ordo')) {
      throw new CLIError('File must have .ordo extension', ErrorType.VALIDATION, 'CLI-001', [
        'Use a file with .ordo extension',
        'Example: ordojs build src/component.ordo'
      ]);
    }

    // Check if file exists
    try {
      await readFile(file);
    } catch (error) {
      throw new CLIError(`File not found: ${file}`, ErrorType.VALIDATION, 'CLI-002', [
        'Check if the file exists',
        'Make sure you have the correct path'
      ]);
    }

    // Read the source file
    const source = await readFile(file);

    // Create compiler with options
    const compiler = new OrdoJSCompiler({
      target: options.target as 'es2022' | 'es2020' | 'es2018',
      optimize: true,
      sourceMaps: options.sourcemap,
      minify: options.minify
    });

    // Compile the source
    logger.info('Compiling...');
    const result = compiler.compile(source);

    if (!result.success) {
      throw new CLIError(
        'Compilation failed',
        ErrorType.COMPILATION,
        'CLI-003',
        result.errors.map(error => error)
      );
    }

    // Optimize CSS if present
    logger.info('Optimizing CSS...');
    const cssOptimizer = new OrdoJSCSSOptimizer({
      removeUnusedRules: true,
      minify: options.minify,
      mergeDuplicateRules: true,
      removeRedundantDeclarations: true,
      optimizeShorthands: true,
      removeEmptyRules: true,
      sortDeclarations: true
    });

    // Initialize CSS-in-JS compiler for any CSS-in-JS expressions
    const cssInJSCompiler = new OrdoJSCSSInJSCompiler({
      scoped: true,
      classPrefix: 'ordojs',
      optimize: options.minify,
      generateSourceMaps: options.sourcemap
    });

    // Create output directory if it doesn't exist
    await mkdir(options.out, { recursive: true });

    // Write output files
    const baseName = path.basename(file, '.ordo');

    if (result.output) {
      // Write JavaScript output
      await writeFile(path.join(options.out, `${baseName}.js`), result.output);
      logger.success(`Generated ${path.join(options.out, `${baseName}.js`)}`);

      // Generate HTML file for the component
      const htmlContent = `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>${baseName}</title>
</head>
<body>
  <div id="app"></div>
  <script type="module">
    import { ${baseName} } from './${baseName}.js';

    // Mount the component
    const app = ${baseName}();
    app.mount(document.getElementById('app'));
  </script>
</body>
</html>`;

      await writeFile(path.join(options.out, `${baseName}.html`), htmlContent);
      logger.success(`Generated ${path.join(options.out, `${baseName}.html`)}`);
    }

    // Generate source maps if enabled
    if (options.sourcemap && result.output) {
      // Simple source map generation
      const sourceMap = {
        version: 3,
        file: `${baseName}.js`,
        sources: [file],
        names: [],
        mappings: '',
        sourceContent: [source]
      };

      await writeFile(path.join(options.out, `${baseName}.js.map`), JSON.stringify(sourceMap));
      logger.success(`Generated ${path.join(options.out, `${baseName}.js.map`)}`);
    }

    // Apply production optimizations if enabled
    if (options.production || options.brotli || options.gzip || options.analyze || options.bundleReport) {
      logger.info('Applying production optimizations...');

      // Initialize asset optimizer with options
      const assetOptimizer = new AssetOptimizer({
        minifyJs: options.minify,
        minifyCss: options.minify,
        brotli: (options.brotli || options.production) ?? false,
        gzip: (options.gzip || options.production) ?? false,
        sizeReport: (options.bundleReport || options.analyze || options.production) ?? false
      });

      // Optimize all assets in the output directory
      logger.info('Optimizing assets...');
      const optimizationResults = await assetOptimizer.optimizeDirectory(options.out);

      // Generate and display size report if enabled
      if (options.bundleReport || options.analyze || options.production) {
        const sizeReport = assetOptimizer.generateSizeReport(optimizationResults);
        logger.info(sizeReport);

        // Write size report to file
        await writeFile(path.join(options.out, 'size-report.txt'), sizeReport);
        logger.success(`Generated ${path.join(options.out, 'size-report.txt')}`);
      }

      // Log compression statistics
      logger.success(`Assets optimized: ${optimizationResults.assets.length} files`);
      logger.success(`Total size reduction: ${Math.round(optimizationResults.overallCompressionRatio * 100)}%`);
      logger.success(`Original size: ${optimizationResults.totalOriginalSizeHuman}`);
      logger.success(`Optimized size: ${optimizationResults.totalMinifiedSizeHuman}`);

      if (options.brotli || options.production) {
        logger.success(`Brotli compressed size: ${optimizationResults.totalBrotliSizeHuman}`);
      }

      if (options.gzip || options.production) {
        logger.success(`Gzip compressed size: ${optimizationResults.totalGzipSizeHuman}`);
      }
    }

    logger.success(`Build completed successfully`);

    // Handle deployment if --deploy option is specified
    if (options.deploy) {
      logger.info(`Preparing deployment with ${options.deploy} adapter...`);

      // Initialize deployment adapter manager
      const adapterManager = new DeploymentAdapterManager();

      // Register available adapters
      adapterManager.registerAdapter(new VercelAdapter());
      adapterManager.registerAdapter(new NetlifyAdapter());
      adapterManager.registerAdapter(new AWSLambdaAdapter());

      // Check if the specified adapter exists
      const adapter = adapterManager.getAdapter(options.deploy);
      if (!adapter) {
        throw new CLIError(`Adapter '${options.deploy}' not found`, ErrorType.VALIDATION, 'CLI-102', [
          'Available adapters: vercel, netlify, aws-lambda',
          'Example: ordojs build --deploy vercel'
        ]);
      }

      // Create deployment configuration
      const deploymentConfig = {
        outputDir: options.out,
        isStatic: false,
        includeServerFunctions: false,
        env: {
          NODE_ENV: 'production'
        }
      };

      // Prepare deployment
      const result = await adapter.prepareDeployment(deploymentConfig);

      if (!result.success) {
        throw new CLIError(`Deployment preparation failed: ${result.error}`, ErrorType.DEPLOYMENT, 'CLI-105');
      }

      // Log generated files
      logger.info('Generated deployment files:');
      for (const file of result.generatedFiles) {
        logger.info(`  - ${file.path}`);
      }

      // Display deployment instructions
      logger.info('\nDeployment Instructions:');
      logger.info(result.instructions);

      // Display deployment URL if available
      if (result.deploymentUrl) {
        logger.success(`\nDeployment URL: ${result.deploymentUrl}`);
      }

      logger.success('Deployment preparation completed successfully');
    }
  } catch (error) {
    if (error instanceof CLIError) {
      logger.error(`${error.type.toUpperCase()} ERROR: ${error.message}`);
      if (error.code) {
        logger.error(`Error code: ${error.code}`);
      }
      if (error.suggestions && error.suggestions.length > 0) {
        logger.info('Suggestions:');
        error.suggestions.forEach(suggestion => {
          logger.info(`  - ${suggestion}`);
        });
      }
    } else {
      logger.error(`Build failed: ${error instanceof Error ? error.message : String(error)}`);
    }
    process.exit(1);
  }
}
/**
 * Static site generation (SSG) build command implementation
 */
export async function staticBuildCommand(
  entryFile: string,
  options: {
    out: string;
    minify: boolean;
    sourcemap: boolean;
    target: string;
    routes: string;
    public: string;
    brotli?: boolean;
    gzip?: boolean;
    analyze?: boolean;
    bundleReport?: boolean;
    optimization?: boolean;
    production?: boolean;
    deploy?: string;
  }
): Promise<void> {
  logger.info(`Building static site from ${entryFile}...`);

  try {
    // Validate file extension
          if (!entryFile.endsWith('.ordo')) {
      throw new CLIError('Entry file must have .ordo extension', ErrorType.VALIDATION, 'CLI-001', [
        'Use a file with .ordo extension',
        'Example: ordojs build --static src/app.ordo'
      ]);
    }

    // Check if entry file exists
    try {
      await readFile(entryFile);
    } catch (error) {
      throw new CLIError(`Entry file not found: ${entryFile}`, ErrorType.VALIDATION, 'CLI-002', [
        'Check if the file exists',
        'Make sure you have the correct path'
      ]);
    }

    // Create output directory if it doesn't exist
    await mkdir(options.out, { recursive: true });

    // Read the entry file
    const entrySource = await readFile(entryFile);

    // Parse the entry file
    const lexer = new OrdoJSLexer(entrySource);
    const tokens = lexer.tokenize();
    const parser = new OrdoJSParser(tokens);
    const entryAst = parser.parse();

    // Create SSR engine
    const ssrEngine = new OrdoJSSSR({
      includeHydrationMarkers: true,
      includeHydrationData: true
    });

    // Register the entry component
    ssrEngine.registerComponent(entryAst);

    // Initialize file-system router
    logger.info(`Scanning routes directory: ${options.routes}`);

    const router = new FileSystemRouter({
      routesDir: options.routes,
              extensions: ['.ordo'],
      generateCatchAll: true,
      baseUrl: '/'
    });

    // Generate routes from file system
    let routes;
    try {
      routes = await router.generateRoutes();
      logger.info(`Found ${routes.length} routes`);
    } catch (error) {
      throw new CLIError(`Failed to generate routes: ${error instanceof Error ? error.message : String(error)}`, ErrorType.VALIDATION, 'CLI-004', [
        'Check if the routes directory exists',
        'Ensure route files have valid .atom syntax',
        'Use --routes option to specify a different routes directory'
      ]);
    }

    // Register all route components with SSR engine
    for (const route of routes) {
      ssrEngine.registerComponent(route.ast);
      logger.info(`Registered route: ${route.path} -> ${route.componentName}`);
    }

    // Configure SSR engine with route configurations
    ssrEngine.options.routes = routes.map((route: any) => ({
      path: route.path,
      component: route.componentName,
      layout: route.layout
    }));

    // Generate static HTML for each route
    logger.info('Generating static HTML for routes...');

    for (const route of routes) {
      // Create the output directory for this route
      const routeOutputDir = path.join(options.out, route.path === '/' ? '' : route.path);
      await mkdir(routeOutputDir, { recursive: true });

      // Generate HTML for this route
      const html = await ssrEngine.renderRoute(route.path);

      // Write the HTML file
      const outputPath = path.join(routeOutputDir, 'index.html');
      await writeFile(outputPath, html);

      logger.success(`Generated ${outputPath}`);
    }

    // Copy public assets if the directory exists
    try {
      const publicStats = await stat(options.public);

      if (publicStats.isDirectory()) {
        logger.info(`Copying public assets from ${options.public}...`);

        // Get all files in the public directory recursively
        async function getAllFiles(dir: string): Promise<string[]> {
          const files: string[] = [];
          const items = await readdir(dir, { withFileTypes: true });

          for (const item of items) {
            const fullPath = path.join(dir, item.name);
            if (item.isDirectory()) {
              files.push(...await getAllFiles(fullPath));
            } else {
              files.push(fullPath);
            }
          }
          return files;
        }

        const publicFiles = await getAllFiles(options.public);

        for (const publicFile of publicFiles) {
          const stats = await stat(publicFile);

          if (stats.isFile()) {
            const relativePath = path.relative(options.public, publicFile);
            const outputPath = path.join(options.out, relativePath);

            // Create directory if it doesn't exist
            const outputDir = path.dirname(outputPath);
            await mkdir(outputDir, { recursive: true });

            // Copy the file
            await copyFile(publicFile, outputPath);
            logger.info(`Copied ${relativePath}`);
          }
        }
      }
    } catch (error) {
      logger.warn(`Public directory not found: ${options.public}`);
    }

    // Generate client-side JavaScript bundle
    logger.info('Generating client-side JavaScript bundle...');

    // Create compiler with options
    const compiler = new OrdoJSCompiler({
      target: options.target as 'es2022' | 'es2020' | 'es2018',
      optimize: true,
      sourceMaps: options.sourcemap,
      minify: options.minify
    });

    // Compile the entry file
    const result = compiler.compile(entrySource);

    if (!result.success) {
      throw new CLIError(
        'Client-side compilation failed',
        ErrorType.COMPILATION,
        'CLI-003',
        result.errors.map(error => error)
      );
    }

    // Optimize CSS for static site generation
    logger.info('Optimizing CSS for static site...');
    const cssOptimizer = new OrdoJSCSSOptimizer({
      removeUnusedRules: true,
      minify: options.minify,
      mergeDuplicateRules: true,
      removeRedundantDeclarations: true,
      optimizeShorthands: true,
      removeEmptyRules: true,
      sortDeclarations: true
    });

    // Initialize CSS-in-JS compiler for static site
    const cssInJSCompiler = new OrdoJSCSSInJSCompiler({
      scoped: true,
      classPrefix: 'ordojs',
      optimize: options.minify,
      generateSourceMaps: options.sourcemap
    });

    // Generate navigation utilities
    logger.info('Generating navigation utilities...');
    const navigationUtils = router.generateNavigationUtils();
    await writeFile(path.join(options.out, 'router.js'), navigationUtils);
    logger.success(`Generated ${path.join(options.out, 'router.js')}`);

    // Generate advanced code splitting configuration using CodeSplitter
    logger.info('Generating code splitting configuration...');

    // Create code splitter
    const codeSplitter = new CodeSplitter({
      enabled: true,
      chunkSizeThreshold: 50000, // 50KB
      routeBasedSplitting: true,
      componentBasedSplitting: true,
      maxChunks: 10,
      alwaysIncludeInMain: []
    });

    // Register all components with the code splitter
    for (const route of routes) {
      codeSplitter.registerComponent(route.ast);
    }

    // Register routes for route-based splitting
    codeSplitter.registerRoutes(routes);

    // Analyze for code splitting
    const codeSplittingResult = codeSplitter.analyze();

    // Write code splitting configuration
    await writeFile(
      path.join(options.out, 'code-splitting.json'),
      JSON.stringify(codeSplittingResult.chunks, null, 2)
    );
    logger.success(`Generated ${path.join(options.out, 'code-splitting.json')}`);

    // Write lazy loading utilities
    await writeFile(
      path.join(options.out, 'lazy-loading.js'),
      codeSplittingResult.lazyLoadingCode
    );
    logger.success(`Generated ${path.join(options.out, 'lazy-loading.js')}`);

    // Write client-side JavaScript
    const baseName = path.basename(entryFile, '.atom');

    if (result.output) {
      // Write JavaScript output
      await writeFile(path.join(options.out, `${baseName}.js`), result.output);
      logger.success(`Generated ${path.join(options.out, `${baseName}.js`)}`);

      // Generate source maps if enabled
      if (options.sourcemap) {
        // Simple source map generation
        const sourceMap = {
          version: 3,
          file: `${baseName}.js`,
          sources: [entryFile],
          names: [],
          mappings: '',
          sourceContent: [entrySource]
        };

        await writeFile(path.join(options.out, `${baseName}.js.map`), JSON.stringify(sourceMap));
        logger.success(`Generated ${path.join(options.out, `${baseName}.js.map`)}`);
      }
    }

    // Apply production optimizations if enabled
    if (options.production || options.brotli || options.gzip || options.analyze || options.bundleReport) {
      logger.info('Applying production optimizations for static site...');

      // Initialize asset optimizer with options
      const assetOptimizer = new AssetOptimizer({
        minifyJs: options.minify,
        minifyCss: options.minify,
        brotli: (options.brotli || options.production) ?? false,
        gzip: (options.gzip || options.production) ?? false,
        sizeReport: (options.bundleReport || options.analyze || options.production) ?? false
      });

      // Optimize all assets in the output directory
      logger.info('Optimizing static site assets...');
      const optimizationResults = await assetOptimizer.optimizeDirectory(options.out);

      // Generate and display size report if enabled
      if (options.bundleReport || options.analyze || options.production) {
        const sizeReport = assetOptimizer.generateSizeReport(optimizationResults);
        logger.info(sizeReport);

        // Write size report to file
        await writeFile(path.join(options.out, 'size-report.txt'), sizeReport);
        logger.success(`Generated ${path.join(options.out, 'size-report.txt')}`);
      }

      // Log compression statistics
      logger.success(`Assets optimized: ${optimizationResults.assets.length} files`);
      logger.success(`Total size reduction: ${Math.round(optimizationResults.overallCompressionRatio * 100)}%`);
      logger.success(`Original size: ${optimizationResults.totalOriginalSizeHuman}`);
      logger.success(`Optimized size: ${optimizationResults.totalMinifiedSizeHuman}`);

      if (options.brotli || options.production) {
        logger.success(`Brotli compressed size: ${optimizationResults.totalBrotliSizeHuman}`);
      }

      if (options.gzip || options.production) {
        logger.success(`Gzip compressed size: ${optimizationResults.totalGzipSizeHuman}`);
      }
    }

    logger.success(`Static site generation completed successfully`);
    logger.info(`Output directory: ${path.resolve(options.out)}`);

    // Handle deployment if --deploy option is specified
    if (options.deploy) {
      logger.info(`Preparing deployment with ${options.deploy} adapter...`);

      // Initialize deployment adapter manager
      const adapterManager = new DeploymentAdapterManager();

      // Register available adapters
      adapterManager.registerAdapter(new VercelAdapter());
      adapterManager.registerAdapter(new NetlifyAdapter());
      adapterManager.registerAdapter(new AWSLambdaAdapter());

      // Check if the specified adapter exists
      const adapter = adapterManager.getAdapter(options.deploy);
      if (!adapter) {
        throw new CLIError(`Adapter '${options.deploy}' not found`, ErrorType.VALIDATION, 'CLI-102', [
          'Available adapters: vercel, netlify, aws-lambda',
          'Example: ordojs build --static --deploy vercel'
        ]);
      }

      // Create deployment configuration
      const deploymentConfig = {
        outputDir: options.out,
        isStatic: true,
        includeServerFunctions: false,
        env: {
          NODE_ENV: 'production'
        }
      };

      // Prepare deployment
      const result = await adapter.prepareDeployment(deploymentConfig);

      if (!result.success) {
        throw new CLIError(`Deployment preparation failed: ${result.error}`, ErrorType.DEPLOYMENT, 'CLI-105');
      }

      // Log generated files
      logger.info('Generated deployment files:');
      for (const file of result.generatedFiles) {
        logger.info(`  - ${file.path}`);
      }

      // Display deployment instructions
      logger.info('\nDeployment Instructions:');
      logger.info(result.instructions);

      // Display deployment URL if available
      if (result.deploymentUrl) {
        logger.success(`\nDeployment URL: ${result.deploymentUrl}`);
      }

      logger.success('Deployment preparation completed successfully');
    }
  } catch (error) {
    if (error instanceof CLIError) {
      throw error;
    } else {
      throw new CLIError(
        `Static site generation failed: ${error instanceof Error ? error.message : String(error)}`,
        ErrorType.COMPILATION,
        'CLI-005'
      );
    }
  }
}
