/**
 * Configuration File Loader
 *
 * Loads and parses JSON/YAML credential configuration files with validation.
 * Supports hot reloading and performance monitoring.
 */

import * as fs from 'fs/promises'
import * as path from 'path'

import {
  IConfigLoader,
  LoadResult,
  ConfigFile,
  Account,
  AccountConfig,
  Platform,
  CredentialErrorCode,
  CredentialManagerError,
} from '@/types/credential'

import { CredentialValidator } from './CredentialValidator'
import { PlatformDetector } from './PlatformDetector'

/**
 * Configuration loader implementation
 */
export class ConfigLoader implements IConfigLoader {
  private validator = new CredentialValidator()
  private platformDetector = new PlatformDetector()
  private watchCallbacks = new Map<string, (accounts: Account[]) => void>()

  /**
   * Load configuration file (JSON or YAML)
   */
  async loadConfig(filePath: string): Promise<LoadResult> {
    const startTime = Date.now()

    try {
      // Validate file path
      if (!filePath || typeof filePath !== 'string' || filePath.trim().length === 0) {
        return {
          success: false,
          accounts: [],
          errors: ['Invalid file path provided'],
          loadTime: Date.now() - startTime,
        }
      }

      // Check if file exists
      try {
        await fs.access(filePath)
      } catch (error) {
        return {
          success: false,
          accounts: [],
          errors: [`Configuration file not found: ${filePath}`],
          loadTime: Date.now() - startTime,
          filePath,
        }
      }

      // Read file contents
      let fileContent: string
      try {
        fileContent = await fs.readFile(filePath, 'utf-8')
      } catch (error) {
        return {
          success: false,
          accounts: [],
          errors: [`Failed to read configuration file: ${error instanceof Error ? error.message : 'Unknown error'}`],
          loadTime: Date.now() - startTime,
          filePath,
        }
      }

      // Parse configuration
      let configData: ConfigFile
      try {
        configData = this.parseConfigFile(fileContent, filePath)
      } catch (error) {
        return {
          success: false,
          accounts: [],
          errors: [`Failed to parse configuration file: ${error instanceof Error ? error.message : 'Unknown error'}`],
          loadTime: Date.now() - startTime,
          filePath,
        }
      }

      // Validate configuration structure
      if (!this.isValidConfigStructure(configData)) {
        return {
          success: false,
          accounts: [],
          errors: ['Invalid configuration file structure'],
          loadTime: Date.now() - startTime,
          filePath,
          version: configData.version,
        }
      }

      // Validate accounts
      const validationResults = this.validator.validateAccountConfigs(configData.accounts)
      const errors: string[] = []
      const validAccounts: Account[] = []

      for (let i = 0; i < configData.accounts.length; i++) {
        const accountConfig = configData.accounts[i]
        const validationResult = validationResults[i]

        if (validationResult.isValid) {
          // Convert config to account
          const account = this.convertConfigToAccount(accountConfig)
          validAccounts.push(account)
        } else {
          errors.push(`Account ${accountConfig?.id || i}: ${validationResult.errors.join(', ')}`)
        }
      }

      const loadTime = Date.now() - startTime

      // Performance check
      if (loadTime >= 100) {
        errors.push(`Configuration load time (${loadTime}ms) exceeds performance requirement (<100ms)`)
      }

      return {
        success: errors.length === 0,
        accounts: validAccounts,
        errors: errors.length > 0 ? errors : undefined,
        loadTime,
        filePath,
        version: configData.version,
      }
    } catch (error) {
      return {
        success: false,
        accounts: [],
        errors: [`Unexpected error loading configuration: ${error instanceof Error ? error.message : 'Unknown error'}`],
        loadTime: Date.now() - startTime,
        filePath,
      }
    }
  }

  /**
   * Watch configuration file for changes
   */
  watchConfig(filePath: string, callback: (accounts: Account[]) => void): void {
    if (!filePath || typeof filePath !== 'string') {
      throw new CredentialManagerError(CredentialErrorCode.FILE_WATCH_FAILED, 'Invalid file path for watching')
    }

    if (typeof callback !== 'function') {
      throw new CredentialManagerError(CredentialErrorCode.FILE_WATCH_FAILED, 'Callback must be a function')
    }

    // Store callback for this file path
    this.watchCallbacks.set(filePath, callback)

    // Note: In a real implementation, this would use fs.watch or chokidar
    // For now, we'll set up a simple polling mechanism for demonstration
    this.setupFileWatching(filePath, callback)
  }

  /**
   * Stop watching configuration files
   */
  stopWatching(): void {
    // Clear all callbacks
    this.watchCallbacks.clear()

    // Note: In a real implementation, this would close file watchers
    // For now, this is a placeholder
  }

  /**
   * Parse configuration file (JSON or YAML)
   */
  private parseConfigFile(content: string, filePath: string): ConfigFile {
    const extension = path.extname(filePath).toLowerCase()

    if (extension === '.json') {
      return this.parseJsonConfig(content)
    } else if (extension === '.yaml' || extension === '.yml') {
      return this.parseYamlConfig(content)
    } else {
      // Try JSON first, then YAML
      try {
        return this.parseJsonConfig(content)
      } catch (jsonError) {
        try {
          return this.parseYamlConfig(content)
        } catch (yamlError) {
          throw new Error(
            `Unsupported file format. Failed to parse as JSON: ${
              jsonError instanceof Error ? jsonError.message : 'Unknown error'
            }. Failed to parse as YAML: ${yamlError instanceof Error ? yamlError.message : 'Unknown error'}`,
          )
        }
      }
    }
  }

  /**
   * Parse JSON configuration
   */
  private parseJsonConfig(content: string): ConfigFile {
    try {
      const parsed = JSON.parse(content)
      return this.normalizeConfig(parsed)
    } catch (error) {
      throw new Error(`Invalid JSON format: ${error instanceof Error ? error.message : 'Unknown error'}`)
    }
  }

  /**
   * Parse YAML configuration (simplified implementation)
   */
  private parseYamlConfig(content: string): ConfigFile {
    // Note: This is a very basic YAML parser for demonstration
    // In production, you would use a proper YAML library like 'js-yaml'
    try {
      // Convert basic YAML to JSON and parse
      const jsonContent = this.basicYamlToJson(content)
      const parsed = JSON.parse(jsonContent)
      return this.normalizeConfig(parsed)
    } catch (error) {
      throw new Error(`Invalid YAML format: ${error instanceof Error ? error.message : 'Unknown error'}`)
    }
  }

  /**
   * Basic YAML to JSON converter (simplified)
   */
  private basicYamlToJson(yaml: string): string {
    // This is a very basic implementation for demonstration
    // In production, use a proper YAML library
    const lines = yaml.split('\n')
    const result: any = {}
    const stack: any[] = [result]
    let currentIndent = 0

    for (const line of lines) {
      const trimmed = line.trim()
      if (!trimmed || trimmed.startsWith('#')) continue

      const indent = line.length - line.trimLeft().length

      if (trimmed.includes(':')) {
        const [key, ...valueParts] = trimmed.split(':')
        const value = valueParts.join(':').trim()

        if (value) {
          // Simple key-value pair
          stack[stack.length - 1][key.trim()] = this.parseYamlValue(value)
        } else {
          // Start of nested object
          const newObj = {}
          stack[stack.length - 1][key.trim()] = newObj
          if (indent > currentIndent) {
            stack.push(newObj)
            currentIndent = indent
          }
        }
      } else if (trimmed.startsWith('-')) {
        // Array item
        const value = trimmed.substring(1).trim()
        const current = stack[stack.length - 1]
        if (!Array.isArray(current.accounts)) {
          current.accounts = []
        }
        current.accounts.push(this.parseYamlValue(value))
      }
    }

    return JSON.stringify(result)
  }

  /**
   * Parse YAML value to appropriate type
   */
  private parseYamlValue(value: string): any {
    const trimmed = value.trim()

    if (trimmed === 'true') return true
    if (trimmed === 'false') return false
    if (trimmed === 'null') return null

    // Try parsing as number
    if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10)
    if (/^\d+\.\d+$/.test(trimmed)) return parseFloat(trimmed)

    // Remove quotes if present
    if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
      return trimmed.slice(1, -1)
    }

    return trimmed
  }

  /**
   * Normalize configuration structure
   */
  private normalizeConfig(data: any): ConfigFile {
    if (!data || typeof data !== 'object') {
      throw new Error('Configuration must be an object')
    }

    return {
      version: data.version || '1.0',
      accounts: Array.isArray(data.accounts) ? data.accounts : [],
      metadata: data.metadata || {},
    }
  }

  /**
   * Validate configuration file structure
   */
  private isValidConfigStructure(config: ConfigFile): boolean {
    if (!config || typeof config !== 'object') {
      return false
    }

    if (!config.version || typeof config.version !== 'string') {
      return false
    }

    if (!Array.isArray(config.accounts)) {
      return false
    }

    return true
  }

  /**
   * Convert account configuration to runtime account
   */
  private convertConfigToAccount(config: AccountConfig): Account {
    // Auto-detect platform if not specified or if mismatch
    let platform = config.platform
    const detectedPlatform = this.platformDetector.detectPlatform(config.credentials)

    if (detectedPlatform && detectedPlatform !== platform) {
      platform = detectedPlatform
    }

    return {
      id: config.id,
      platform,
      name: config.name,
      credentials: config.credentials,
      enabled: config.enabled !== false, // Default to true
      metadata: config.metadata || {},
      createdAt: new Date(),
    }
  }

  /**
   * Setup file watching (simplified implementation)
   */
  private setupFileWatching(filePath: string, callback: (accounts: Account[]) => void): void {
    // Note: In a real implementation, this would use fs.watch or chokidar
    // This is a simplified polling-based approach for demonstration

    let lastModified = 0

    const checkFile = async () => {
      try {
        const stats = await fs.stat(filePath)
        const currentModified = stats.mtime.getTime()

        if (currentModified > lastModified && lastModified > 0) {
          // File has been modified
          const result = await this.loadConfig(filePath)
          if (result.success) {
            callback(result.accounts)
          }
        }

        lastModified = currentModified
      } catch (error) {
        // File might have been deleted or is temporarily unavailable
        // In a real implementation, you'd handle this more gracefully
      }
    }

    // Initial check
    checkFile()

    // Set up periodic checking (every 100ms for demo - in practice use proper file watching)
    const interval = setInterval(checkFile, 100)

    // Store interval for cleanup (in a real implementation)
    // this.watchIntervals.set(filePath, interval);
  }

  /**
   * Get supported file formats
   */
  getSupportedFormats(): string[] {
    return ['.json', '.yaml', '.yml']
  }

  /**
   * Validate file format
   */
  isSupportedFormat(filePath: string): boolean {
    const extension = path.extname(filePath).toLowerCase()
    return this.getSupportedFormats().includes(extension)
  }

  /**
   * Get configuration file template
   */
  getConfigTemplate(): ConfigFile {
    return {
      version: '1.0',
      accounts: [
        {
          id: 'example-pacifica-account',
          platform: Platform.PACIFICA,
          name: 'Example Pacifica Account',
          credentials: {
            type: 'ed25519',
            privateKey: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
          },
          enabled: true,
          metadata: {
            description: 'Example account for Pacifica platform',
          },
        },
        {
          id: 'example-binance-account',
          platform: Platform.BINANCE,
          name: 'Example Binance Account',
          credentials: {
            type: 'hmac',
            apiKey: 'your-binance-api-key-here',
            secretKey: 'your-binance-secret-key-here',
          },
          enabled: true,
          metadata: {
            description: 'Example account for Binance platform',
          },
        },
      ],
      metadata: {
        created: new Date().toISOString(),
        description: 'Example credential configuration file',
      },
    }
  }
}
