import * as fs from 'fs';
import * as path from 'path';
import * as dotenv from 'dotenv';
import { AppConfig, ConfigValidationResult } from './interfaces';

export class ConfigManager {
  private static instance: ConfigManager;
  private config: AppConfig | null = null;
  private readonly configPath: string;

  constructor() {
    this.configPath = path.join(process.cwd(), 'config');
    this.loadEnvironmentVariables();
  }

  private loadEnvironmentVariables(): void {
    const environment = process.env.NODE_ENV || 'development';
    
    // Load .env file first (base variables)
    const envPath = path.join(process.cwd(), '.env');
    if (fs.existsSync(envPath)) {
      dotenv.config({ path: envPath });
    }

    // Load environment-specific .env file
    const envSpecificPath = path.join(process.cwd(), `.env.${environment}`);
    if (fs.existsSync(envSpecificPath)) {
      dotenv.config({ path: envSpecificPath });
    }

    // Load local .env file (for overrides)
    const localEnvPath = path.join(process.cwd(), '.env.local');
    if (fs.existsSync(localEnvPath)) {
      dotenv.config({ path: localEnvPath });
    }
  }

  static getInstance(): ConfigManager {
    if (!ConfigManager.instance) {
      ConfigManager.instance = new ConfigManager();
    }
    return ConfigManager.instance;
  }

  /**
   * Load configuration for the current environment
   */
  async load(environment: string = process.env.NODE_ENV || 'development'): Promise<AppConfig> {
    if (this.config) {
      return this.config;
    }

    try {
      // Load environment-specific config
      const envConfigPath = path.join(this.configPath, `${environment}.ts`);
      let envConfig: Partial<AppConfig> = {};

      if (fs.existsSync(envConfigPath)) {
        // In a real implementation, you'd import the config file
        // For now, we'll use a basic structure
        envConfig = await this.loadEnvironmentConfig(environment);
      }

      // Load base config
      const baseConfig = await this.loadBaseConfig();

      // Merge configurations
      this.config = this.mergeConfigs(baseConfig, envConfig);

      // Validate configuration
      const validation = this.validate(this.config);
      if (!validation.isValid) {
        throw new Error(`Configuration validation failed: ${validation.errors.join(', ')}`);
      }

      return this.config;
    } catch (error: any) {
      throw new Error(`Failed to load configuration: ${error.message}`);
    }
  }

  /**
   * Get current configuration
   */
  get(): AppConfig {
    if (!this.config) {
      throw new Error('Configuration not loaded. Call load() first.');
    }
    return this.config;
  }

  /**
   * Reload configuration
   */
  async reload(environment?: string): Promise<AppConfig> {
    this.config = null;
    return await this.load(environment);
  }

  /**
   * Validate configuration
   */
  validate(config: AppConfig): ConfigValidationResult {
    const errors: string[] = [];
    const warnings: string[] = [];

    // Validate required fields
    if (!config.port || config.port <= 0) {
      errors.push('Port must be a positive number');
    }

    if (!config.host) {
      errors.push('Host is required');
    }

    if (!config.auth?.jwtSecret) {
      errors.push('JWT secret is required');
    }

    if (!config.database?.host) {
      errors.push('Database host is required');
    }

    // Validate auth providers
    if (config.auth?.providers?.firebase?.enabled && !config.auth.providers.firebase.projectId) {
      errors.push('Firebase project ID is required when Firebase auth is enabled');
    }

    // Validate notification providers
    if (config.notifications?.email?.enabled && !config.notifications.email.defaults?.from) {
      warnings.push('Email from address is recommended when email notifications are enabled');
    }

    return {
      isValid: errors.length === 0,
      errors,
      warnings
    };
  }

  /**
   * Get configuration for a specific module
   */
  getModuleConfig(moduleName: string): any {
    const config = this.get();
    return config.plugins?.find(p => p.name === moduleName)?.config || {};
  }

  /**
   * Update configuration dynamically
   */
  async updateConfig(updates: Partial<AppConfig>): Promise<void> {
    if (!this.config) {
      throw new Error('Configuration not loaded');
    }

    this.config = { ...this.config, ...updates };
    
    // Validate updated config
    const validation = this.validate(this.config);
    if (!validation.isValid) {
      throw new Error(`Configuration validation failed: ${validation.errors.join(', ')}`);
    }
  }

  private async loadEnvironmentConfig(environment: string): Promise<Partial<AppConfig>> {
    return {
      environment: environment as any,
      port: parseInt(process.env.PORT || '3000'),
      host: process.env.HOST || 'localhost',
      logging: {
        level: (process.env.LOG_LEVEL || (environment === 'production' ? 'warn' : 'debug')) as 'warn' | 'debug' | 'error' | 'info',
        file: process.env.LOG_FILE || 'logs/app.log'
      },
      cors: {
        origin: process.env.CORS_ORIGIN || '*',
        credentials: process.env.CORS_CREDENTIALS === 'true'
      },
      security: {
        rateLimit: {
          windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000'),
          max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || '100')
        },
        helmet: process.env.HELMET_ENABLED !== 'false',
        compression: process.env.COMPRESSION_ENABLED !== 'false'
      },
      database: {
        host: process.env.DB_HOST || 'localhost',
        port: parseInt(process.env.DB_PORT || '3306'),
        username: process.env.DB_USERNAME || 'root',
        password: process.env.DB_PASSWORD || '',
        database: process.env.DB_DATABASE || 'diagramers',
        dialect: (process.env.DB_DIALECT || 'mysql') as 'mysql' | 'postgres' | 'sqlite' | 'mariadb' | 'mongodb',
        logging: process.env.DB_LOGGING === 'true'
      },
      auth: {
        jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
        jwtExpiresIn: process.env.JWT_EXPIRES_IN || '1h',
        refreshTokenExpiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || '7d',
        providers: {
          internal: true,
          firebase: {
            enabled: process.env.FIREBASE_ENABLED === 'true',
            projectId: process.env.FIREBASE_PROJECT_ID || '',
            privateKeyId: process.env.FIREBASE_PRIVATE_KEY_ID || '',
            privateKey: process.env.FIREBASE_PRIVATE_KEY || '',
            clientEmail: process.env.FIREBASE_CLIENT_EMAIL || '',
            clientId: process.env.FIREBASE_CLIENT_ID || '',
            authUri: process.env.FIREBASE_AUTH_URI || '',
            tokenUri: process.env.FIREBASE_TOKEN_URI || '',
            authProviderX509CertUrl: process.env.FIREBASE_AUTH_PROVIDER_X509_CERT_URL || '',
            clientX509CertUrl: process.env.FIREBASE_CLIENT_X509_CERT_URL || ''
          },
          oauth: {
            enabled: process.env.OAUTH_ENABLED === 'true',
            providers: {
              google: {
                enabled: process.env.GOOGLE_CLIENT_ID ? true : false,
                clientId: process.env.GOOGLE_CLIENT_ID || '',
                clientSecret: process.env.GOOGLE_CLIENT_SECRET || '',
                callbackUrl: process.env.GOOGLE_CALLBACK_URL || '',
                scope: ['email', 'profile']
              },
              github: {
                enabled: false,
                clientId: '',
                clientSecret: '',
                callbackUrl: '',
                scope: []
              },
              facebook: {
                enabled: false,
                clientId: '',
                clientSecret: '',
                callbackUrl: '',
                scope: []
              }
            }
          },
          sms: {
            enabled: process.env.SMS_ENABLED === 'true',
            provider: (process.env.SMS_PROVIDER || 'twilio') as 'twilio' | 'aws-sns' | 'custom',
            twilio: {
              accountSid: process.env.TWILIO_ACCOUNT_SID || '',
              authToken: process.env.TWILIO_AUTH_TOKEN || '',
              fromNumber: process.env.TWILIO_PHONE_NUMBER || ''
            }
          },
          email: {
            enabled: process.env.EMAIL_ENABLED === 'true',
            provider: 'nodemailer'
          }
        }
      },
      notifications: {
        email: {
          enabled: process.env.EMAIL_ENABLED === 'true',
          provider: 'nodemailer',
          templates: {
            path: './templates/email',
            engine: 'handlebars'
          },
          defaults: {
            from: process.env.EMAIL_FROM || '',
            replyTo: process.env.EMAIL_FROM || ''
          }
        },
        sms: {
          enabled: process.env.SMS_ENABLED === 'true',
          provider: 'twilio',
          defaults: {
            from: process.env.TWILIO_PHONE_NUMBER || ''
          }
        },
        push: {
          enabled: false,
          provider: 'firebase'
        }
      }
    };
  }

  private async loadBaseConfig(): Promise<Partial<AppConfig>> {
    // Load base configuration
    return {
      environment: 'development',
      port: parseInt(process.env.PORT || '3000'),
      host: 'localhost',
      cors: {
        origin: '*',
        credentials: false
      },
      database: {
        host: 'localhost',
        port: 3306,
        username: 'root',
        password: '',
        database: 'diagramers',
        dialect: 'mysql',
        logging: false
      },
      auth: {
        jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
        jwtExpiresIn: '1h',
        refreshTokenExpiresIn: '7d',
        providers: {
          internal: true,
          firebase: {
            enabled: false,
            projectId: '',
            privateKeyId: '',
            privateKey: '',
            clientEmail: '',
            clientId: '',
            authUri: '',
            tokenUri: '',
            authProviderX509CertUrl: '',
            clientX509CertUrl: ''
          },
          oauth: {
            enabled: false,
            providers: {
              google: {
                enabled: false,
                clientId: '',
                clientSecret: '',
                callbackUrl: '',
                scope: []
              },
              github: {
                enabled: false,
                clientId: '',
                clientSecret: '',
                callbackUrl: '',
                scope: []
              },
              facebook: {
                enabled: false,
                clientId: '',
                clientSecret: '',
                callbackUrl: '',
                scope: []
              }
            }
          },
          sms: {
            enabled: false,
            provider: 'twilio'
          },
          email: {
            enabled: false,
            provider: 'nodemailer'
          }
        }
      },
      notifications: {
        email: {
          enabled: false,
          provider: 'nodemailer',
          templates: {
            path: './templates/email',
            engine: 'handlebars'
          },
          defaults: {
            from: '',
            replyTo: ''
          }
        },
        sms: {
          enabled: false,
          provider: 'twilio',
          defaults: {
            from: ''
          }
        },
        push: {
          enabled: false,
          provider: 'firebase'
        }
      },
      plugins: [],
      logging: {
        level: 'info'
      },
      security: {
        rateLimit: {
          windowMs: 15 * 60 * 1000, // 15 minutes
          max: 100
        },
        helmet: true,
        compression: true
      }
    };
  }

  private mergeConfigs(base: Partial<AppConfig>, env: Partial<AppConfig>): AppConfig {
    // Deep merge configurations
    return {
      ...base,
      ...env,
      auth: {
        ...base.auth,
        ...env.auth,
        providers: {
          ...base.auth?.providers,
          ...env.auth?.providers
        }
      },
      notifications: {
        ...base.notifications,
        ...env.notifications
      },
      database: {
        ...base.database,
        ...env.database
      }
    } as AppConfig;
  }
} 