/**
 * Base Credential Validator
 *
 * Comprehensive validation for all credential types with platform-specific
 * validation rules and security checks.
 */

import {
  Platform,
  Credentials,
  Ed25519Credentials,
  Eip191Credentials,
  HmacCredentials,
  AccountConfig,
  CredentialErrorCode,
  CredentialManagerError,
} from '@/types/credential'

/**
 * Validation result interface
 */
export interface ValidationResult {
  isValid: boolean
  errors: string[]
  warnings: string[]
  platform?: Platform
  credentialType?: string
}

/**
 * Base credential validator with platform-specific validation
 */
export class CredentialValidator {
  /**
   * Validate account configuration
   * @param config Account configuration to validate
   * @returns Validation result
   */
  validateAccountConfig(config: any): ValidationResult {
    const result: ValidationResult = {
      isValid: true,
      errors: [],
      warnings: [],
    }

    // Basic structure validation
    if (!config || typeof config !== 'object') {
      result.errors.push('Account configuration must be an object')
      result.isValid = false
      return result
    }

    // Required fields
    if (!config.id || typeof config.id !== 'string') {
      result.errors.push('Account ID is required and must be a string')
      result.isValid = false
    } else if (config.id.length === 0 || config.id.trim().length === 0) {
      result.errors.push('Account ID cannot be empty')
      result.isValid = false
    } else if (config.id.length > 100) {
      result.errors.push('Account ID cannot exceed 100 characters')
      result.isValid = false
    }

    // Platform validation
    if (!config.platform) {
      result.errors.push('Platform is required')
      result.isValid = false
    } else if (!Object.values(Platform).includes(config.platform)) {
      result.errors.push(`Invalid platform: ${config.platform}. Must be one of: ${Object.values(Platform).join(', ')}`)
      result.isValid = false
    } else {
      result.platform = config.platform
    }

    // Name validation
    if (!config.name || typeof config.name !== 'string') {
      result.errors.push('Account name is required and must be a string')
      result.isValid = false
    } else if (config.name.trim().length === 0) {
      result.errors.push('Account name cannot be empty')
      result.isValid = false
    }

    // Credentials validation
    if (!config.credentials) {
      result.errors.push('Credentials are required')
      result.isValid = false
    } else {
      const credentialValidation = this.validateCredentials(config.credentials, config.platform)
      result.errors.push(...credentialValidation.errors)
      result.warnings.push(...credentialValidation.warnings)
      result.credentialType = credentialValidation.credentialType

      if (!credentialValidation.isValid) {
        result.isValid = false
      }
    }

    // Optional field validation
    if (config.enabled !== undefined && typeof config.enabled !== 'boolean') {
      result.warnings.push('Account enabled flag should be a boolean, defaulting to true')
    }

    if (config.metadata !== undefined && typeof config.metadata !== 'object') {
      result.warnings.push('Account metadata should be an object, ignoring')
    }

    return result
  }

  /**
   * Validate credentials based on platform
   * @param credentials Credentials to validate
   * @param platform Expected platform
   * @returns Validation result
   */
  validateCredentials(credentials: any, platform?: Platform): ValidationResult {
    const result: ValidationResult = {
      isValid: true,
      errors: [],
      warnings: [],
    }

    if (!credentials || typeof credentials !== 'object') {
      result.errors.push('Credentials must be an object')
      result.isValid = false
      return result
    }

    if (!credentials.type || typeof credentials.type !== 'string') {
      result.errors.push('Credential type is required')
      result.isValid = false
      return result
    }

    result.credentialType = credentials.type

    // Platform-specific validation
    switch (credentials.type) {
      case 'ed25519':
        this.validateEd25519Credentials(credentials, result)
        if (platform && platform !== Platform.PACIFICA) {
          result.warnings.push(
            `Ed25519 credentials typically used with Pacifica platform, but account platform is ${platform}`,
          )
        }
        break

      case 'eip191':
        this.validateEip191Credentials(credentials, result)
        if (platform && platform !== Platform.ASTER) {
          result.warnings.push(
            `EIP-191 credentials typically used with Aster platform, but account platform is ${platform}`,
          )
        }
        break

      case 'hmac':
        this.validateHmacCredentials(credentials, result)
        if (platform && platform !== Platform.BINANCE) {
          result.warnings.push(
            `HMAC credentials typically used with Binance platform, but account platform is ${platform}`,
          )
        }
        break

      default:
        result.errors.push(`Unsupported credential type: ${credentials.type}`)
        result.isValid = false
    }

    return result
  }

  /**
   * Validate Ed25519 credentials for Pacifica platform
   */
  private validateEd25519Credentials(credentials: any, result: ValidationResult): void {
    // Private key validation
    if (!credentials.privateKey) {
      result.errors.push('Ed25519 credentials require a privateKey')
      result.isValid = false
      return
    }

    if (typeof credentials.privateKey !== 'string') {
      result.errors.push('Ed25519 privateKey must be a string')
      result.isValid = false
      return
    }

    const privateKey = credentials.privateKey.toLowerCase()

    // Check format: 64 hex characters (32 bytes)
    if (!/^[0-9a-f]{64}$/.test(privateKey)) {
      result.errors.push('Ed25519 privateKey must be 64 hexadecimal characters (32 bytes)')
      result.isValid = false
    }

    // Security checks
    if (privateKey === '0'.repeat(64)) {
      result.errors.push('Ed25519 privateKey cannot be all zeros')
      result.isValid = false
    }

    if (privateKey === 'f'.repeat(64)) {
      result.errors.push('Ed25519 privateKey cannot be all ones')
      result.isValid = false
    }

    // Check for weak keys (common patterns)
    const weakPatterns = ['1'.repeat(64), '123456789abcdef'.repeat(4), 'deadbeef'.repeat(8)]

    for (const pattern of weakPatterns) {
      if (privateKey === pattern) {
        result.warnings.push('Ed25519 privateKey appears to be a weak/test key')
        break
      }
    }

    // Public key validation (if provided)
    if (credentials.publicKey) {
      if (typeof credentials.publicKey !== 'string') {
        result.warnings.push('Ed25519 publicKey should be a string')
      } else {
        // Base58 format validation (typical for Pacifica)
        if (!/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(credentials.publicKey)) {
          result.warnings.push('Ed25519 publicKey format may be invalid (expected base58)')
        }
      }
    }
  }

  /**
   * Validate EIP-191 credentials for Aster platform
   */
  private validateEip191Credentials(credentials: any, result: ValidationResult): void {
    // Private key validation
    if (!credentials.privateKey) {
      result.errors.push('EIP-191 credentials require a privateKey')
      result.isValid = false
      return
    }

    if (typeof credentials.privateKey !== 'string') {
      result.errors.push('EIP-191 privateKey must be a string')
      result.isValid = false
      return
    }

    const privateKey = credentials.privateKey.toLowerCase()

    // Check format: 0x + 64 hex characters
    if (!/^0x[0-9a-f]{64}$/.test(privateKey)) {
      result.errors.push('EIP-191 privateKey must be 0x followed by 64 hexadecimal characters')
      result.isValid = false
    }

    // Security checks
    if (privateKey === '0x' + '0'.repeat(64)) {
      result.errors.push('EIP-191 privateKey cannot be all zeros')
      result.isValid = false
    }

    if (privateKey === '0x' + 'f'.repeat(64)) {
      result.errors.push('EIP-191 privateKey cannot be all ones')
      result.isValid = false
    }

    // Check for weak keys
    const weakPatterns = [
      '0x' + '1'.repeat(64),
      '0x' + '123456789abcdef0'.repeat(4),
      '0x' + 'deadbeefdeadbeef'.repeat(4),
    ]

    for (const pattern of weakPatterns) {
      if (privateKey === pattern) {
        result.warnings.push('EIP-191 privateKey appears to be a weak/test key')
        break
      }
    }

    // Address validation (if provided)
    if (credentials.address) {
      if (typeof credentials.address !== 'string') {
        result.warnings.push('EIP-191 address should be a string')
      } else {
        const address = credentials.address.toLowerCase()
        if (!/^0x[0-9a-f]{40}$/.test(address)) {
          result.warnings.push('EIP-191 address format may be invalid (expected 0x + 40 hex chars)')
        }
      }
    }

    // Public key validation (if provided)
    if (credentials.publicKey) {
      if (typeof credentials.publicKey !== 'string') {
        result.warnings.push('EIP-191 publicKey should be a string')
      } else {
        const publicKey = credentials.publicKey.toLowerCase()
        if (!/^0x[0-9a-f]{128}$/.test(publicKey)) {
          result.warnings.push('EIP-191 publicKey format may be invalid (expected 0x + 128 hex chars for uncompressed)')
        }
      }
    }
  }

  /**
   * Validate HMAC credentials for Binance platform
   */
  private validateHmacCredentials(credentials: any, result: ValidationResult): void {
    // API key validation
    if (!credentials.apiKey) {
      result.errors.push('HMAC credentials require an apiKey')
      result.isValid = false
    } else if (typeof credentials.apiKey !== 'string') {
      result.errors.push('HMAC apiKey must be a string')
      result.isValid = false
    } else if (credentials.apiKey.length < 16) {
      result.errors.push('HMAC apiKey must be at least 16 characters')
      result.isValid = false
    } else if (credentials.apiKey.length > 128) {
      result.warnings.push('HMAC apiKey is unusually long (>128 characters)')
    }

    // Secret key validation
    if (!credentials.secretKey) {
      result.errors.push('HMAC credentials require a secretKey')
      result.isValid = false
    } else if (typeof credentials.secretKey !== 'string') {
      result.errors.push('HMAC secretKey must be a string')
      result.isValid = false
    } else if (credentials.secretKey.length < 16) {
      result.errors.push('HMAC secretKey must be at least 16 characters')
      result.isValid = false
    } else if (credentials.secretKey.length > 128) {
      result.warnings.push('HMAC secretKey is unusually long (>128 characters)')
    }

    // Security checks
    if (credentials.apiKey && credentials.secretKey) {
      if (credentials.apiKey === credentials.secretKey) {
        result.errors.push('HMAC apiKey and secretKey cannot be the same')
        result.isValid = false
      }

      // Check for test/demo keys
      const testPatterns = ['test', 'demo', 'sample', 'example', '123456', 'abcdef']

      for (const pattern of testPatterns) {
        if (
          credentials.apiKey.toLowerCase().includes(pattern) ||
          credentials.secretKey.toLowerCase().includes(pattern)
        ) {
          result.warnings.push('HMAC credentials appear to be test/demo keys')
          break
        }
      }

      // Binance-specific format check
      const binanceApiKeyPattern = /^[A-Za-z0-9]{64}$/
      const binanceSecretKeyPattern = /^[A-Za-z0-9]{64}$/

      if (binanceApiKeyPattern.test(credentials.apiKey) && binanceSecretKeyPattern.test(credentials.secretKey)) {
        // Valid Binance format
      } else {
        result.warnings.push('HMAC credentials may not be in standard Binance format')
      }
    }
  }

  /**
   * Validate multiple account configurations
   * @param configs Array of account configurations
   * @returns Array of validation results
   */
  validateAccountConfigs(configs: any[]): ValidationResult[] {
    if (!Array.isArray(configs)) {
      return [
        {
          isValid: false,
          errors: ['Account configurations must be an array'],
          warnings: [],
        },
      ]
    }

    const results = configs.map(config => this.validateAccountConfig(config))

    // Check for duplicate IDs
    const ids = new Set<string>()
    const duplicateIds = new Set<string>()

    for (let i = 0; i < configs.length; i++) {
      const config = configs[i]
      if (config && config.id) {
        if (ids.has(config.id)) {
          duplicateIds.add(config.id)
          results[i].errors.push(`Duplicate account ID: ${config.id}`)
          results[i].isValid = false
        } else {
          ids.add(config.id)
        }
      }
    }

    // Mark all instances of duplicate IDs as invalid
    for (let i = 0; i < configs.length; i++) {
      const config = configs[i]
      if (config && config.id && duplicateIds.has(config.id)) {
        if (!results[i].errors.includes(`Duplicate account ID: ${config.id}`)) {
          results[i].errors.push(`Duplicate account ID: ${config.id}`)
          results[i].isValid = false
        }
      }
    }

    return results
  }

  /**
   * Generate comprehensive validation report
   * @param configs Account configurations to validate
   * @returns Detailed validation report
   */
  generateValidationReport(configs: any[]): {
    isValid: boolean
    totalAccounts: number
    validAccounts: number
    invalidAccounts: number
    platformCounts: Record<Platform, number>
    credentialTypeCounts: Record<string, number>
    globalErrors: string[]
    globalWarnings: string[]
    accountResults: ValidationResult[]
  } {
    const results = this.validateAccountConfigs(configs)

    const report = {
      isValid: results.every(r => r.isValid),
      totalAccounts: configs.length,
      validAccounts: results.filter(r => r.isValid).length,
      invalidAccounts: results.filter(r => !r.isValid).length,
      platformCounts: {
        [Platform.PACIFICA]: 0,
        [Platform.ASTER]: 0,
        [Platform.BINANCE]: 0,
      },
      credentialTypeCounts: {} as Record<string, number>,
      globalErrors: [] as string[],
      globalWarnings: [] as string[],
      accountResults: results,
    }

    // Count platforms and credential types
    for (let i = 0; i < configs.length; i++) {
      const config = configs[i]
      const result = results[i]

      if (result.platform) {
        report.platformCounts[result.platform]++
      }

      if (result.credentialType) {
        report.credentialTypeCounts[result.credentialType] =
          (report.credentialTypeCounts[result.credentialType] || 0) + 1
      }
    }

    // Global warnings
    if (report.totalAccounts === 0) {
      report.globalWarnings.push('No accounts configured')
    }

    if (report.invalidAccounts > 0) {
      report.globalErrors.push(`${report.invalidAccounts} account(s) have validation errors`)
    }

    const totalPlatforms = Object.values(report.platformCounts).filter(count => count > 0).length
    if (totalPlatforms === 0) {
      report.globalWarnings.push('No valid platforms detected')
    } else if (totalPlatforms === 1) {
      report.globalWarnings.push('Only one platform type configured')
    }

    return report
  }

  /**
   * Sanitize credentials for logging (remove sensitive data)
   * @param credentials Credentials to sanitize
   * @returns Sanitized credentials safe for logging
   */
  sanitizeCredentials(credentials: any): any {
    if (!credentials || typeof credentials !== 'object') {
      return credentials
    }

    const sanitized = { ...credentials }

    // Remove or mask sensitive fields
    if (sanitized.privateKey) {
      sanitized.privateKey = '***REDACTED***'
    }

    if (sanitized.secretKey) {
      sanitized.secretKey = '***REDACTED***'
    }

    if (sanitized.apiKey) {
      // Show first 8 characters for debugging
      sanitized.apiKey = sanitized.apiKey.substring(0, 8) + '***REDACTED***'
    }

    return sanitized
  }
}
