/**
 * Signer Factory Implementation
 *
 * Creates and manages platform-specific signers for the credential manager.
 * Implements the factory pattern to provide unified access to different
 * signing implementations (Pacifica, Binance, Aster).
 */

import {
  Platform,
  Account,
  SignResult,
  Credentials,
  CredentialManagerError,
  ErrorType,
} from '../../types/credential.js'

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

// Import platform-specific signers
import { AsterSigner } from './signers/AsterSigner.js'
import { BinanceSigner } from './signers/BinanceSigner.js'
import { PacificaSigner } from './signers/PacificaSigner.js'

// ============================================================================
// Types and Interfaces
// ============================================================================

export interface ISignerStrategy {
  readonly platform: Platform
  registerAccount(accountId: string, credentials: Credentials): Promise<void> | void
  removeAccount(accountId: string): boolean
  sign(accountId: string, message: Uint8Array): Promise<SignResult>
  verify(accountId: string, message: Uint8Array, signature: string): Promise<boolean>
  getMetrics?(): any
  resetMetrics?(): void
}

export interface SignerFactoryOptions {
  enablePlatformDetection?: boolean
  platformDetector?: PlatformDetector
  enableMetrics?: boolean
  logErrors?: boolean
}

// ============================================================================
// Signer Factory Implementation
// ============================================================================

export class SignerFactory {
  private strategies = new Map<Platform, ISignerStrategy>()
  private platformDetector: PlatformDetector
  private options: Required<SignerFactoryOptions>
  private metrics = {
    totalSignRequests: 0,
    successfulSigns: 0,
    failedSigns: 0,
    platformUsage: new Map<Platform, number>(),
  }

  constructor(options: SignerFactoryOptions = {}) {
    this.options = {
      enablePlatformDetection: options.enablePlatformDetection ?? true,
      platformDetector: options.platformDetector ?? new PlatformDetector(),
      enableMetrics: options.enableMetrics ?? true,
      logErrors: options.logErrors ?? true,
    }

    this.platformDetector = this.options.platformDetector
    this.initializeStrategies()
  }

  /**
   * Initialize all platform-specific signing strategies
   */
  private initializeStrategies(): void {
    try {
      // Initialize Pacifica signer
      const pacificaSigner = new PacificaSigner()
      this.registerStrategy(Platform.PACIFICA, pacificaSigner)

      // Initialize Binance signer
      const binanceSigner = new BinanceSigner()
      this.registerStrategy(Platform.BINANCE, binanceSigner)

      // Initialize Aster signer
      const asterSigner = new AsterSigner()
      this.registerStrategy(Platform.ASTER, asterSigner)
    } catch (error) {
      throw new CredentialManagerError(
        `Failed to initialize signing strategies: ${error.message}`,
        ErrorType.SIGNATURE_ERROR,
      )
    }
  }

  /**
   * Register a platform-specific signing strategy
   */
  registerStrategy(platform: Platform, strategy: ISignerStrategy): void {
    if (strategy.platform !== platform) {
      throw new CredentialManagerError(
        `Strategy platform mismatch: expected ${platform}, got ${strategy.platform}`,
        ErrorType.VALIDATION_ERROR,
      )
    }

    this.strategies.set(platform, strategy)

    // Initialize platform usage tracking
    if (this.options.enableMetrics && !this.metrics.platformUsage.has(platform)) {
      this.metrics.platformUsage.set(platform, 0)
    }
  }

  /**
   * Get strategy for a specific platform
   */
  getStrategy(platform: Platform): ISignerStrategy {
    const strategy = this.strategies.get(platform)
    if (!strategy) {
      throw new CredentialManagerError(
        `No signing strategy available for platform: ${platform}`,
        ErrorType.PLATFORM_DETECTION_ERROR,
      )
    }
    return strategy
  }

  /**
   * Register an account with the appropriate signer
   */
  async registerAccount(account: Account): Promise<void> {
    try {
      // Detect platform if not explicitly specified or if detection is enabled
      const platform = account.platform

      if (this.options.enablePlatformDetection) {
        const detectedPlatform = this.platformDetector.detectPlatform(account.credentials)
        if (detectedPlatform && detectedPlatform !== account.platform) {
          // Log platform mismatch but use the specified platform
          if (this.options.logErrors) {
            console.warn(
              `Platform mismatch for account ${account.id}: specified ${account.platform}, detected ${detectedPlatform}`,
            )
          }
        }
      }

      const strategy = this.getStrategy(platform)
      await strategy.registerAccount(account.id, account.credentials)
    } catch (error) {
      if (this.options.logErrors) {
        console.error(`Failed to register account ${account.id}:`, error)
      }
      throw error
    }
  }

  /**
   * Remove an account from all signers
   */
  removeAccount(accountId: string): boolean {
    let removed = false

    for (const strategy of this.strategies.values()) {
      const wasRemoved = strategy.removeAccount(accountId)
      removed = removed || wasRemoved
    }

    return removed
  }

  /**
   * Sign a message using the appropriate platform strategy
   */
  async sign(accountId: string, message: Uint8Array, platform?: Platform): Promise<SignResult> {
    this.updateMetrics('sign-request')

    try {
      // If platform is not specified, try to find it from registered accounts
      let targetPlatform = platform

      if (!targetPlatform) {
        targetPlatform = await this.findAccountPlatform(accountId)
      }

      const strategy = this.getStrategy(targetPlatform)
      const result = await strategy.sign(accountId, message)

      if (result.success) {
        this.updateMetrics('sign-success', targetPlatform)
      } else {
        this.updateMetrics('sign-failure')
        if (this.options.logErrors && result.error) {
          console.error(`Signing failed for account ${accountId}:`, result.error)
        }
      }

      return result
    } catch (error) {
      this.updateMetrics('sign-failure')

      if (this.options.logErrors) {
        console.error(`Sign operation failed for account ${accountId}:`, error)
      }

      return {
        success: false,
        algorithm: 'unknown',
        timestamp: new Date(),
        error: error instanceof Error ? error.message : 'Unknown signing error',
      }
    }
  }

  /**
   * Verify a signature using the appropriate platform strategy
   */
  async verify(accountId: string, message: Uint8Array, signature: string, platform?: Platform): Promise<boolean> {
    try {
      // If platform is not specified, try to find it from registered accounts
      let targetPlatform = platform

      if (!targetPlatform) {
        targetPlatform = await this.findAccountPlatform(accountId)
      }

      const strategy = this.getStrategy(targetPlatform)
      return await strategy.verify(accountId, message, signature)
    } catch (error) {
      if (this.options.logErrors) {
        console.error(`Verification failed for account ${accountId}:`, error)
      }
      return false
    }
  }

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

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

  /**
   * Get factory metrics
   */
  getMetrics() {
    if (!this.options.enableMetrics) {
      return null
    }

    const platformMetrics: Record<string, any> = {}

    // Collect metrics from individual strategies
    for (const [platform, strategy] of this.strategies.entries()) {
      if (strategy.getMetrics) {
        platformMetrics[platform] = strategy.getMetrics()
      }
    }

    return {
      factory: {
        totalSignRequests: this.metrics.totalSignRequests,
        successfulSigns: this.metrics.successfulSigns,
        failedSigns: this.metrics.failedSigns,
        successRate:
          this.metrics.totalSignRequests > 0 ? this.metrics.successfulSigns / this.metrics.totalSignRequests : 0,
        platformUsage: Object.fromEntries(this.metrics.platformUsage),
      },
      strategies: platformMetrics,
    }
  }

  /**
   * Reset all metrics
   */
  resetMetrics(): void {
    if (!this.options.enableMetrics) {
      return
    }

    this.metrics = {
      totalSignRequests: 0,
      successfulSigns: 0,
      failedSigns: 0,
      platformUsage: new Map<Platform, number>(),
    }

    // Reset strategy metrics
    for (const strategy of this.strategies.values()) {
      if (strategy.resetMetrics) {
        strategy.resetMetrics()
      }
    }
  }

  /**
   * Get detailed platform information
   */
  getPlatformInfo(platform: Platform): any {
    const strategy = this.strategies.get(platform)
    if (!strategy) {
      return null
    }

    return {
      platform: strategy.platform,
      hasMetrics: typeof strategy.getMetrics === 'function',
      supportsReset: typeof strategy.resetMetrics === 'function',
      isAvailable: true,
    }
  }

  /**
   * Batch register multiple accounts
   */
  async registerAccounts(accounts: Account[]): Promise<void> {
    const errors: Error[] = []

    // Process accounts in parallel by platform
    const platformGroups = new Map<Platform, Account[]>()

    for (const account of accounts) {
      const group = platformGroups.get(account.platform) || []
      group.push(account)
      platformGroups.set(account.platform, group)
    }

    // Register accounts for each platform
    const registrationPromises = Array.from(platformGroups.entries()).map(async ([platform, platformAccounts]) => {
      const strategy = this.getStrategy(platform)

      for (const account of platformAccounts) {
        try {
          await strategy.registerAccount(account.id, account.credentials)
        } catch (error) {
          errors.push(new Error(`Failed to register ${account.id}: ${error.message}`))
        }
      }
    })

    await Promise.all(registrationPromises)

    if (errors.length > 0) {
      throw new CredentialManagerError(
        `Failed to register ${errors.length} accounts: ${errors.map(e => e.message).join(', ')}`,
        ErrorType.VALIDATION_ERROR,
      )
    }
  }

  // ============================================================================
  // Private Helper Methods
  // ============================================================================

  /**
   * Find which platform an account belongs to
   */
  private async findAccountPlatform(accountId: string): Promise<Platform> {
    // Try to find the account by attempting operations on each platform
    for (const [platform, strategy] of this.strategies.entries()) {
      try {
        // Try a lightweight operation to check if account exists
        // This is a simplified approach - in production, you might want
        // to maintain an account-to-platform mapping
        await strategy.sign(accountId, new Uint8Array(0))
        return platform
      } catch (error) {
        // Account not found on this platform, continue
        continue
      }
    }

    throw new CredentialManagerError(`Account ${accountId} not found on any platform`, ErrorType.SIGNATURE_ERROR)
  }

  /**
   * Update internal metrics
   */
  private updateMetrics(type: 'sign-request' | 'sign-success' | 'sign-failure', platform?: Platform): void {
    if (!this.options.enableMetrics) {
      return
    }

    switch (type) {
      case 'sign-request':
        this.metrics.totalSignRequests++
        break
      case 'sign-success':
        this.metrics.successfulSigns++
        if (platform) {
          const currentCount = this.metrics.platformUsage.get(platform) || 0
          this.metrics.platformUsage.set(platform, currentCount + 1)
        }
        break
      case 'sign-failure':
        this.metrics.failedSigns++
        break
    }
  }
}

// ============================================================================
// Utility Functions
// ============================================================================

/**
 * Create a pre-configured signer factory instance
 */
export function createSignerFactory(options?: SignerFactoryOptions): SignerFactory {
  return new SignerFactory(options)
}

/**
 * Create signer factory with all platforms enabled
 */
export function createDefaultSignerFactory(): SignerFactory {
  return new SignerFactory({
    enablePlatformDetection: true,
    enableMetrics: true,
    logErrors: true,
  })
}

/**
 * Create signer factory for testing (no error logging)
 */
export function createTestSignerFactory(): SignerFactory {
  return new SignerFactory({
    enablePlatformDetection: true,
    enableMetrics: false,
    logErrors: false,
  })
}

// ============================================================================
// Export
// ============================================================================

export default SignerFactory
