/**
 * Main Credential Manager Implementation
 *
 * Central orchestrator for multi-platform credential management.
 * Provides unified interface for configuration loading, account management,
 * and signing operations across different platforms.
 */

import {
  ICredentialManager,
  LoadResult,
  Account,
  SignResult,
  Platform,
  CredentialErrorCode,
  CredentialManagerError,
  PerformanceMetrics,
} from '@/types/credential'

import { ConfigLoader } from './ConfigLoader'
import { CredentialValidator } from './CredentialValidator'
import { PlatformDetector } from './PlatformDetector'
import { PacificaSigner } from './signers/PacificaSigner'

/**
 * Main credential manager class
 */
export class CredentialManager implements ICredentialManager {
  private configLoader = new ConfigLoader()
  private validator = new CredentialValidator()
  private platformDetector = new PlatformDetector()
  private accounts = new Map<string, Account>()
  private signers = new Map<Platform, any>()
  private isWatching = false
  private currentConfigPath?: string
  private metrics: PerformanceMetrics

  constructor() {
    // Initialize performance metrics
    this.metrics = {
      configLoadTime: 0,
      configWatchLatency: 0,
      signOperationsTotal: 0,
      signOperationsSuccess: 0,
      signOperationsFailed: 0,
      averageSignTime: 0,
      maxSignTime: 0,
      minSignTime: Infinity,
      verifyOperationsTotal: 0,
      verifyOperationsSuccess: 0,
      verifyOperationsFailed: 0,
      averageVerifyTime: 0,
      memoryUsage: 0,
      accountCount: 0,
      errorsByCode: Object.fromEntries(Object.values(CredentialErrorCode).map(code => [code, 0])) as Record<
        CredentialErrorCode,
        number
      >,
      lastResetAt: new Date(),
      uptime: 0,
    }

    // Initialize signers
    this.initializeSigners()
  }

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

    try {
      // Load configuration using ConfigLoader
      const result = await this.configLoader.loadConfig(filePath)

      // Update metrics
      const loadTime = Date.now() - startTime
      this.metrics.configLoadTime = loadTime

      if (result.success) {
        // Clear existing accounts
        this.accounts.clear()

        // Add loaded accounts to internal storage
        for (const account of result.accounts) {
          this.accounts.set(account.id, account)
        }

        this.metrics.accountCount = this.accounts.size
        this.currentConfigPath = filePath

        // Validate all accounts have supported platforms
        for (const account of result.accounts) {
          if (!this.signers.has(account.platform)) {
            this.recordError(CredentialErrorCode.PLATFORM_NOT_SUPPORTED)
            console.warn(`Platform ${account.platform} not supported for account ${account.id}`)
          }
        }
      } else {
        this.recordError(CredentialErrorCode.CONFIG_VALIDATION_FAILED)
      }

      return result
    } catch (error) {
      const loadTime = Date.now() - startTime
      this.metrics.configLoadTime = loadTime
      this.recordError(CredentialErrorCode.CONFIG_NOT_FOUND)

      throw new CredentialManagerError(
        CredentialErrorCode.CONFIG_NOT_FOUND,
        `Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`,
        error,
      )
    }
  }

  /**
   * Watch configuration file for changes
   */
  watchConfig(filePath: string, callback: (accounts: Account[]) => void): void {
    try {
      if (this.isWatching && this.currentConfigPath !== filePath) {
        // Stop watching previous file
        this.stopWatching()
      }

      // Set up file watching
      this.configLoader.watchConfig(filePath, accounts => {
        const startTime = Date.now()

        // Update internal account storage
        this.accounts.clear()
        for (const account of accounts) {
          this.accounts.set(account.id, account)
        }

        this.metrics.accountCount = this.accounts.size
        this.metrics.configWatchLatency = Date.now() - startTime

        // Call user callback
        callback(accounts)
      })

      this.isWatching = true
      this.currentConfigPath = filePath
    } catch (error) {
      this.recordError(CredentialErrorCode.FILE_WATCH_FAILED)
      throw new CredentialManagerError(
        CredentialErrorCode.FILE_WATCH_FAILED,
        `Failed to watch configuration file: ${error instanceof Error ? error.message : 'Unknown error'}`,
        error,
      )
    }
  }

  /**
   * Stop watching configuration file
   */
  stopWatching(): void {
    try {
      this.configLoader.stopWatching()
      this.isWatching = false
      this.currentConfigPath = undefined
    } catch (error) {
      console.warn('Error stopping configuration watching:', error)
    }
  }

  /**
   * Get account by ID
   */
  getAccount(accountId: string): Account | null {
    if (!accountId || typeof accountId !== 'string') {
      return null
    }

    return this.accounts.get(accountId) || null
  }

  /**
   * List all accounts
   */
  listAccounts(): Account[] {
    return Array.from(this.accounts.values())
  }

  /**
   * Sign message with account
   */
  async sign(accountId: string, message: Uint8Array): Promise<SignResult> {
    const startTime = Date.now()
    this.metrics.signOperationsTotal++

    try {
      // Validate inputs
      if (!accountId || typeof accountId !== 'string') {
        throw new CredentialManagerError(CredentialErrorCode.ACCOUNT_NOT_FOUND, 'Invalid account ID')
      }

      if (!message || message.length === 0) {
        throw new CredentialManagerError(CredentialErrorCode.INVALID_MESSAGE, 'Message cannot be empty')
      }

      // Get account
      const account = this.getAccount(accountId)
      if (!account) {
        throw new CredentialManagerError(CredentialErrorCode.ACCOUNT_NOT_FOUND, `Account not found: ${accountId}`)
      }

      // Check if account is enabled
      if (!account.enabled) {
        throw new CredentialManagerError(CredentialErrorCode.ACCOUNT_DISABLED, `Account is disabled: ${accountId}`)
      }

      // Get appropriate signer
      const signer = this.signers.get(account.platform)
      if (!signer) {
        throw new CredentialManagerError(
          CredentialErrorCode.PLATFORM_NOT_SUPPORTED,
          `Platform not supported: ${account.platform}`,
        )
      }

      // Perform signing
      const signature = await signer.sign(message, account.credentials)

      const duration = Date.now() - startTime
      this.updateSigningMetrics(true, duration)

      // Update account last used time
      account.lastUsed = new Date()

      return {
        success: true,
        signature,
        algorithm: this.getAlgorithmForPlatform(account.platform),
        timestamp: new Date(),
        duration,
      }
    } catch (error) {
      const duration = Date.now() - startTime
      this.updateSigningMetrics(false, duration)

      if (error instanceof CredentialManagerError) {
        this.recordError(error.code)
        return {
          success: false,
          algorithm: this.getAlgorithmForPlatform(Platform.PACIFICA), // Default
          timestamp: new Date(),
          duration,
          error: error.message,
        }
      }

      this.recordError(CredentialErrorCode.SIGNING_FAILED)
      return {
        success: false,
        algorithm: this.getAlgorithmForPlatform(Platform.PACIFICA), // Default
        timestamp: new Date(),
        duration,
        error: `Signing failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
      }
    }
  }

  /**
   * Verify signature
   */
  async verify(accountId: string, message: Uint8Array, signature: string): Promise<boolean> {
    const startTime = Date.now()
    this.metrics.verifyOperationsTotal++

    try {
      // Validate inputs
      if (!accountId || !message || !signature) {
        this.metrics.verifyOperationsFailed++
        return false
      }

      // Get account
      const account = this.getAccount(accountId)
      if (!account) {
        this.metrics.verifyOperationsFailed++
        return false
      }

      // Get appropriate signer
      const signer = this.signers.get(account.platform)
      if (!signer) {
        this.metrics.verifyOperationsFailed++
        return false
      }

      // Get public key for verification
      let publicKey: string
      try {
        publicKey = await this.getPublicKeyForAccount(account)
      } catch (error) {
        this.metrics.verifyOperationsFailed++
        return false
      }

      // Perform verification
      const isValid = await signer.verify(message, signature, publicKey)

      const duration = Date.now() - startTime
      this.updateVerificationMetrics(isValid, duration)

      return isValid
    } catch (error) {
      const duration = Date.now() - startTime
      this.updateVerificationMetrics(false, duration)
      return false
    }
  }

  /**
   * Get performance metrics
   */
  getMetrics(): PerformanceMetrics {
    // Update uptime
    this.metrics.uptime = Date.now() - this.metrics.lastResetAt.getTime()

    // Update memory usage if available
    if (process.memoryUsage) {
      this.metrics.memoryUsage = process.memoryUsage().heapUsed
    }

    return { ...this.metrics }
  }

  /**
   * Reset performance metrics
   */
  resetMetrics(): void {
    this.metrics = {
      configLoadTime: 0,
      configWatchLatency: 0,
      signOperationsTotal: 0,
      signOperationsSuccess: 0,
      signOperationsFailed: 0,
      averageSignTime: 0,
      maxSignTime: 0,
      minSignTime: Infinity,
      verifyOperationsTotal: 0,
      verifyOperationsSuccess: 0,
      verifyOperationsFailed: 0,
      averageVerifyTime: 0,
      memoryUsage: this.metrics.memoryUsage,
      accountCount: this.accounts.size,
      errorsByCode: {},
      lastResetAt: new Date(),
      uptime: 0,
    }
  }

  /**
   * Health check
   */
  async healthCheck(): Promise<{
    healthy: boolean
    components: {
      configLoader: boolean
      accounts: boolean
      signers: Record<Platform, boolean>
    }
    performance: {
      configLoadTime: number
      averageSignTime: number
      memoryUsage: number
    }
    timestamp: Date
  }> {
    const health = {
      healthy: true,
      components: {
        configLoader: true,
        accounts: this.accounts.size > 0,
        signers: {
          [Platform.PACIFICA]: this.signers.has(Platform.PACIFICA),
          [Platform.ASTER]: this.signers.has(Platform.ASTER),
          [Platform.BINANCE]: this.signers.has(Platform.BINANCE),
        },
      },
      performance: {
        configLoadTime: this.metrics.configLoadTime,
        averageSignTime: this.metrics.averageSignTime,
        memoryUsage: this.metrics.memoryUsage,
      },
      timestamp: new Date(),
    }

    // Check overall health
    health.healthy =
      health.components.configLoader &&
      Object.values(health.components.signers).some(Boolean) &&
      health.performance.configLoadTime < 100 &&
      health.performance.averageSignTime < 50

    return health
  }

  /**
   * Initialize platform signers
   */
  private initializeSigners(): void {
    // Initialize Pacifica signer
    this.signers.set(Platform.PACIFICA, new PacificaSigner())

    // TODO: Initialize other signers when implemented
    // this.signers.set(Platform.BINANCE, new BinanceSigner());
    // this.signers.set(Platform.ASTER, new AsterSigner());
  }

  /**
   * Get algorithm for platform
   */
  private getAlgorithmForPlatform(platform: Platform): string {
    switch (platform) {
      case Platform.PACIFICA:
        return 'ed25519'
      case Platform.ASTER:
        return 'eip191'
      case Platform.BINANCE:
        return 'hmac-sha256'
      default:
        return 'unknown'
    }
  }

  /**
   * Get public key for account
   */
  private async getPublicKeyForAccount(account: Account): Promise<string> {
    switch (account.platform) {
      case Platform.PACIFICA:
        if (account.credentials.type === 'ed25519' && 'privateKey' in account.credentials) {
          // Derive public key from private key
          const { PacificaKeyUtils } = await import('./signers/PacificaSigner')
          return PacificaKeyUtils.derivePublicKey(account.credentials.privateKey)
        }
        break

      case Platform.ASTER:
        // TODO: Implement Aster public key derivation
        break

      case Platform.BINANCE:
        // HMAC doesn't use public keys for verification
        return ''
    }

    throw new CredentialManagerError(
      CredentialErrorCode.ACCOUNT_INVALID_CREDENTIALS,
      `Cannot derive public key for platform ${account.platform}`,
    )
  }

  /**
   * Update signing metrics
   */
  private updateSigningMetrics(success: boolean, duration: number): void {
    if (success) {
      this.metrics.signOperationsSuccess++
    } else {
      this.metrics.signOperationsFailed++
    }

    // Update timing metrics
    this.metrics.maxSignTime = Math.max(this.metrics.maxSignTime, duration)
    this.metrics.minSignTime = Math.min(this.metrics.minSignTime, duration)

    // Calculate rolling average
    const totalSuccessfulSigns = this.metrics.signOperationsSuccess
    if (totalSuccessfulSigns > 0) {
      this.metrics.averageSignTime =
        (this.metrics.averageSignTime * (totalSuccessfulSigns - 1) + duration) / totalSuccessfulSigns
    }
  }

  /**
   * Update verification metrics
   */
  private updateVerificationMetrics(success: boolean, duration: number): void {
    if (success) {
      this.metrics.verifyOperationsSuccess++
    } else {
      this.metrics.verifyOperationsFailed++
    }

    // Calculate rolling average for verification time
    const totalVerifications = this.metrics.verifyOperationsTotal
    if (totalVerifications > 0) {
      this.metrics.averageVerifyTime =
        (this.metrics.averageVerifyTime * (totalVerifications - 1) + duration) / totalVerifications
    }
  }

  /**
   * Record error in metrics
   */
  private recordError(errorCode: CredentialErrorCode): void {
    this.metrics.errorsByCode[errorCode] = (this.metrics.errorsByCode[errorCode] || 0) + 1
  }

  /**
   * Validate account credentials
   */
  validateAccount(accountId: string): {
    isValid: boolean
    errors: string[]
    warnings: string[]
  } {
    const account = this.getAccount(accountId)
    if (!account) {
      return {
        isValid: false,
        errors: [`Account not found: ${accountId}`],
        warnings: [],
      }
    }

    return this.validator.validateAccountConfig(account)
  }

  /**
   * Get supported platforms
   */
  getSupportedPlatforms(): Platform[] {
    return Array.from(this.signers.keys())
  }

  /**
   * Check if platform is supported
   */
  isPlatformSupported(platform: Platform): boolean {
    return this.signers.has(platform)
  }
}
