/**
 * @fileoverview OrdoJS CLI - Deployment adapter manager
 * Manages deployment adapters and provides a plugin architecture
 */

import path from 'path';
import { readFile, writeFile } from '../fs.js';
import { logger } from '../logger.js';
import { DeploymentAdapter, DeploymentConfig, DeploymentResult } from './adapter-interface.js';

/**
 * Deployment adapter manager configuration
 */
export interface AdapterManagerConfig {
  /**
   * Directory to store adapter configurations
   */
  configDir?: string;

  /**
   * Default adapter to use
   */
  defaultAdapter?: string;
}

/**
 * Default adapter manager configuration
 */
const DEFAULT_CONFIG: AdapterManagerConfig = {
      configDir: '.ordojs/deployment',
  defaultAdapter: 'vercel'
};

/**
 * Deployment adapter manager
 * Manages deployment adapters and provides a plugin architecture
 */
export class DeploymentAdapterManager {
  /**
   * Registered adapters
   */
  private adapters: Map<string, DeploymentAdapter> = new Map();

  /**
   * Adapter manager configuration
   */
  private config: AdapterManagerConfig;

  /**
   * Create a new deployment adapter manager
   * @param config Adapter manager configuration
   */
  constructor(config: AdapterManagerConfig = {}) {
    this.config = { ...DEFAULT_CONFIG, ...config };
  }

  /**
   * Register a deployment adapter
   * @param adapter Deployment adapter
   */
  registerAdapter(adapter: DeploymentAdapter): void {
    if (this.adapters.has(adapter.name)) {
      logger.warn(`Adapter '${adapter.name}' is already registered. Overwriting...`);
    }

    this.adapters.set(adapter.name, adapter);
    logger.debug(`Registered deployment adapter: ${adapter.name}`);
  }

  /**
   * Get a deployment adapter by name
   * @param name Adapter name
   * @returns Deployment adapter
   */
  getAdapter(name: string): DeploymentAdapter | undefined {
    return this.adapters.get(name);
  }

  /**
   * Get all registered adapters
   * @returns Array of deployment adapters
   */
  getAllAdapters(): DeploymentAdapter[] {
    return Array.from(this.adapters.values());
  }

  /**
   * Get the default adapter
   * @returns Default deployment adapter
   */
  getDefaultAdapter(): DeploymentAdapter | undefined {
    return this.adapters.get(this.config.defaultAdapter || 'vercel');
  }

  /**
   * Set the default adapter
   * @param name Adapter name
   */
  setDefaultAdapter(name: string): void {
    if (!this.adapters.has(name)) {
      throw new Error(`Adapter '${name}' is not registered`);
    }

    this.config.defaultAdapter = name;
    logger.debug(`Set default deployment adapter to: ${name}`);
  }

  /**
   * Load deployment configuration from file
   * @param target Deployment target name
   * @returns Deployment configuration
   */
  async loadDeploymentConfig(target: string): Promise<DeploymentConfig> {
    try {
      const configPath = path.join(this.config.configDir || '.ordojs/deployment', `${target}.json`);
      const configContent = await readFile(configPath);
      return JSON.parse(configContent);
    } catch (error) {
      logger.warn(`Failed to load deployment configuration for target '${target}': ${error instanceof Error ? error.message : String(error)}`);
      return {
        outputDir: 'dist'
      };
    }
  }

  /**
   * Save deployment configuration to file
   * @param target Deployment target name
   * @param config Deployment configuration
   */
  async saveDeploymentConfig(target: string, config: DeploymentConfig): Promise<void> {
    try {
      const configDir = this.config.configDir || '.ordojs/deployment';
      const configPath = path.join(configDir, `${target}.json`);
      await writeFile(configPath, JSON.stringify(config, null, 2));
      logger.debug(`Saved deployment configuration for target '${target}'`);
    } catch (error) {
      logger.error(`Failed to save deployment configuration for target '${target}': ${error instanceof Error ? error.message : String(error)}`);
      throw error;
    }
  }

  /**
   * Prepare deployment using the specified adapter
   * @param adapterName Adapter name
   * @param config Deployment configuration
   * @returns Deployment result
   */
  async prepareDeployment(adapterName: string, config: DeploymentConfig): Promise<DeploymentResult> {
    const adapter = this.getAdapter(adapterName);

    if (!adapter) {
      return {
        success: false,
        error: `Adapter '${adapterName}' not found`,
        generatedFiles: [],
        instructions: `Adapter '${adapterName}' not found. Available adapters: ${Array.from(this.adapters.keys()).join(', ')}`
      };
    }

    // Validate configuration
    const validation = adapter.validateConfig(config);

    if (!validation.valid) {
      return {
        success: false,
        error: `Invalid deployment configuration: ${validation.errors?.join(', ')}`,
        generatedFiles: [],
        instructions: `Please fix the configuration errors and try again: ${validation.errors?.join(', ')}`
      };
    }

    // Prepare deployment
    try {
      const result = await adapter.prepareDeployment(config);

      // Log success or failure
      if (result.success) {
        logger.success(`Deployment preparation for '${adapterName}' successful`);
      } else {
        logger.error(`Deployment preparation for '${adapterName}' failed: ${result.error}`);
      }

      return result;
    } catch (error) {
      logger.error(`Deployment preparation for '${adapterName}' failed with an exception: ${error instanceof Error ? error.message : String(error)}`);

      return {
        success: false,
        error: `Deployment preparation failed: ${error instanceof Error ? error.message : String(error)}`,
        generatedFiles: [],
        instructions: 'An error occurred during deployment preparation. Please check the logs for details.'
      };
    }
  }

  /**
   * Optimize for deployment using the specified adapter
   * @param adapterName Adapter name
   * @param config Deployment configuration
   * @returns Optimization results
   */
  async optimizeForDeployment(adapterName: string, config: DeploymentConfig): Promise<any> {
    const adapter = this.getAdapter(adapterName);

    if (!adapter) {
      throw new Error(`Adapter '${adapterName}' not found`);
    }

    return adapter.optimizeForDeployment(config, config.outputDir);
  }
}
