/**
 * Configuration Schema Validator Implementation
 *
 * Validates credential configuration files against defined schemas
 * to ensure data integrity and proper format before loading.
 * Supports JSON and YAML configuration formats.
 */
import { ConfigFile } from '@/types/credential'

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

export interface ValidationResult {
  valid: boolean
  errors: ValidationError[]
  warnings: ValidationWarning[]
  normalizedConfig?: ConfigFile
}

export interface ValidationError {
  path: string
  message: string
  code: ValidationErrorCode
  value?: any
}

export interface ValidationWarning {
  path: string
  message: string
  code: ValidationWarningCode
  value?: any
}

export enum ValidationErrorCode {
  MISSING_REQUIRED_FIELD = 'MISSING_REQUIRED_FIELD',
  INVALID_TYPE = 'INVALID_TYPE',
  INVALID_FORMAT = 'INVALID_FORMAT',
  INVALID_ENUM_VALUE = 'INVALID_ENUM_VALUE',
  DUPLICATE_ACCOUNT_ID = 'DUPLICATE_ACCOUNT_ID',
  INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
  UNSUPPORTED_VERSION = 'UNSUPPORTED_VERSION',
  INVALID_PLATFORM = 'INVALID_PLATFORM',
}

export enum ValidationWarningCode {
  DEPRECATED_FIELD = 'DEPRECATED_FIELD',
  MISSING_OPTIONAL_FIELD = 'MISSING_OPTIONAL_FIELD',
  UNUSUAL_VALUE = 'UNUSUAL_VALUE',
  PERFORMANCE_CONCERN = 'PERFORMANCE_CONCERN',
}

export interface ValidatorOptions {
  strictMode?: boolean
  allowUnknownFields?: boolean
  normalizeData?: boolean
  validateCredentials?: boolean
  maxAccounts?: number
}

// ============================================================================
// Schema Definitions
// ============================================================================

const SUPPORTED_VERSIONS = ['1.0']

const PACIFICA_PRIVATE_KEY_PATTERN = /^[0-9a-fA-F]{64}$/
const ASTER_PRIVATE_KEY_PATTERN = /^0x[0-9a-fA-F]{64}$/
const ETHEREUM_ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/
const BINANCE_API_KEY_PATTERN = /^[A-Za-z0-9]{64}$/

// ============================================================================
// Configuration Validator Implementation
// ============================================================================

export class ConfigValidator {
  private options: Required<ValidatorOptions>

  constructor(options: ValidatorOptions = {}) {
    this.options = {
      strictMode: options.strictMode ?? false,
      allowUnknownFields: options.allowUnknownFields ?? true,
      normalizeData: options.normalizeData ?? true,
      validateCredentials: options.validateCredentials ?? true,
      maxAccounts: options.maxAccounts ?? 1000,
    }
  }

  /**
   * Validate configuration file content
   */
  validateConfig(config: any): ValidationResult {
    const errors: ValidationError[] = []
    const warnings: ValidationWarning[] = []
    let normalizedConfig: ConfigFile | undefined

    try {
      // Basic structure validation
      this.validateBasicStructure(config, errors)

      if (errors.length > 0 && this.options.strictMode) {
        return { valid: false, errors, warnings }
      }

      // Version validation
      this.validateVersion(config.version, errors, warnings)

      // Accounts validation
      if (config.accounts) {
        this.validateAccounts(config.accounts, errors, warnings)
      }

      // Normalize data if requested and no critical errors
      if (this.options.normalizeData && errors.length === 0) {
        normalizedConfig = this.normalizeConfig(config)
      }

      const valid = errors.length === 0

      return {
        valid,
        errors,
        warnings,
        normalizedConfig,
      }
    } catch (error) {
      errors.push({
        path: 'root',
        message: `Validation failed: ${error.message}`,
        code: ValidationErrorCode.INVALID_FORMAT,
      })

      return { valid: false, errors, warnings }
    }
  }

  /**
   * Validate a single account configuration
   */
  validateAccount(account: any, accountIndex?: number): ValidationResult {
    const errors: ValidationError[] = []
    const warnings: ValidationWarning[] = []
    const pathPrefix = accountIndex !== undefined ? `accounts[${accountIndex}]` : 'account'

    this.validateSingleAccount(account, pathPrefix, errors, warnings)

    return {
      valid: errors.length === 0,
      errors,
      warnings,
    }
  }

  /**
   * Validate credentials for a specific platform
   */
  validateCredentials(platform: Platform, credentials: any): ValidationResult {
    const errors: ValidationError[] = []
    const warnings: ValidationWarning[] = []

    this.validateCredentialsByPlatform(platform, credentials, 'credentials', errors, warnings)

    return {
      valid: errors.length === 0,
      errors,
      warnings,
    }
  }

  /**
   * Quick validation check (returns boolean only)
   */
  isValid(config: any): boolean {
    const result = this.validateConfig(config)
    return result.valid
  }

  // ============================================================================
  // Private Validation Methods
  // ============================================================================

  /**
   * Validate basic configuration structure
   */
  private validateBasicStructure(config: any, errors: ValidationError[]): void {
    if (typeof config !== 'object' || config === null) {
      errors.push({
        path: 'root',
        message: 'Configuration must be an object',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof config,
      })
      return
    }

    // Required fields
    if (!config.version) {
      errors.push({
        path: 'version',
        message: 'Version field is required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    }

    if (!config.accounts) {
      errors.push({
        path: 'accounts',
        message: 'Accounts field is required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    } else if (!Array.isArray(config.accounts)) {
      errors.push({
        path: 'accounts',
        message: 'Accounts must be an array',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof config.accounts,
      })
    }
  }

  /**
   * Validate configuration version
   */
  private validateVersion(version: any, errors: ValidationError[], warnings: ValidationWarning[]): void {
    if (typeof version !== 'string') {
      errors.push({
        path: 'version',
        message: 'Version must be a string',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof version,
      })
      return
    }

    if (!SUPPORTED_VERSIONS.includes(version)) {
      errors.push({
        path: 'version',
        message: `Unsupported version: ${version}. Supported versions: ${SUPPORTED_VERSIONS.join(', ')}`,
        code: ValidationErrorCode.UNSUPPORTED_VERSION,
        value: version,
      })
    }
  }

  /**
   * Validate accounts array
   */
  private validateAccounts(accounts: any[], errors: ValidationError[], warnings: ValidationWarning[]): void {
    if (accounts.length === 0) {
      warnings.push({
        path: 'accounts',
        message: 'No accounts defined',
        code: ValidationWarningCode.UNUSUAL_VALUE,
        value: 0,
      })
      return
    }

    if (accounts.length > this.options.maxAccounts) {
      errors.push({
        path: 'accounts',
        message: `Too many accounts: ${accounts.length}. Maximum allowed: ${this.options.maxAccounts}`,
        code: ValidationErrorCode.INVALID_FORMAT,
        value: accounts.length,
      })
    }

    // Check for duplicate account IDs
    const accountIds = new Set<string>()
    const duplicates = new Set<string>()

    accounts.forEach((account, index) => {
      if (account && account.id) {
        if (accountIds.has(account.id)) {
          duplicates.add(account.id)
        }
        accountIds.add(account.id)
      }

      this.validateSingleAccount(account, `accounts[${index}]`, errors, warnings)
    })

    // Report duplicate IDs
    duplicates.forEach(id => {
      errors.push({
        path: 'accounts',
        message: `Duplicate account ID: ${id}`,
        code: ValidationErrorCode.DUPLICATE_ACCOUNT_ID,
        value: id,
      })
    })

    // Performance warning for large number of accounts
    if (accounts.length > 100) {
      warnings.push({
        path: 'accounts',
        message: `Large number of accounts (${accounts.length}) may impact performance`,
        code: ValidationWarningCode.PERFORMANCE_CONCERN,
        value: accounts.length,
      })
    }
  }

  /**
   * Validate a single account
   */
  private validateSingleAccount(
    account: any,
    pathPrefix: string,
    errors: ValidationError[],
    warnings: ValidationWarning[],
  ): void {
    if (typeof account !== 'object' || account === null) {
      errors.push({
        path: pathPrefix,
        message: 'Account must be an object',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof account,
      })
      return
    }

    // Required fields
    if (!account.id) {
      errors.push({
        path: `${pathPrefix}.id`,
        message: 'Account ID is required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    } else if (typeof account.id !== 'string') {
      errors.push({
        path: `${pathPrefix}.id`,
        message: 'Account ID must be a string',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof account.id,
      })
    } else if (account.id.length === 0) {
      errors.push({
        path: `${pathPrefix}.id`,
        message: 'Account ID cannot be empty',
        code: ValidationErrorCode.INVALID_FORMAT,
        value: account.id,
      })
    }

    // Platform validation
    if (!account.platform) {
      errors.push({
        path: `${pathPrefix}.platform`,
        message: 'Platform is required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    } else if (!Object.values(Platform).includes(account.platform)) {
      errors.push({
        path: `${pathPrefix}.platform`,
        message: `Invalid platform: ${account.platform}. Valid platforms: ${Object.values(Platform).join(', ')}`,
        code: ValidationErrorCode.INVALID_PLATFORM,
        value: account.platform,
      })
    }

    // Credentials validation
    if (!account.credentials) {
      errors.push({
        path: `${pathPrefix}.credentials`,
        message: 'Credentials are required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    } else if (this.options.validateCredentials && account.platform) {
      this.validateCredentialsByPlatform(
        account.platform,
        account.credentials,
        `${pathPrefix}.credentials`,
        errors,
        warnings,
      )
    }

    // Metadata validation (optional)
    if (account.metadata !== undefined) {
      if (typeof account.metadata !== 'object' || account.metadata === null) {
        errors.push({
          path: `${pathPrefix}.metadata`,
          message: 'Metadata must be an object',
          code: ValidationErrorCode.INVALID_TYPE,
          value: typeof account.metadata,
        })
      }
    }

    // Check for unknown fields in strict mode
    if (this.options.strictMode && !this.options.allowUnknownFields) {
      const allowedFields = ['id', 'platform', 'credentials', 'metadata']
      const unknownFields = Object.keys(account).filter(key => !allowedFields.includes(key))

      unknownFields.forEach(field => {
        errors.push({
          path: `${pathPrefix}.${field}`,
          message: `Unknown field: ${field}`,
          code: ValidationErrorCode.INVALID_FORMAT,
          value: account[field],
        })
      })
    }
  }

  /**
   * Validate credentials based on platform
   */
  private validateCredentialsByPlatform(
    platform: Platform,
    credentials: any,
    pathPrefix: string,
    errors: ValidationError[],
    warnings: ValidationWarning[],
  ): void {
    if (typeof credentials !== 'object' || credentials === null) {
      errors.push({
        path: pathPrefix,
        message: 'Credentials must be an object',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof credentials,
      })
      return
    }

    switch (platform) {
      case Platform.PACIFICA:
        this.validatePacificaCredentials(credentials, pathPrefix, errors, warnings)
        break

      case Platform.ASTER:
        this.validateAsterCredentials(credentials, pathPrefix, errors, warnings)
        break

      case Platform.BINANCE:
        this.validateBinanceCredentials(credentials, pathPrefix, errors, warnings)
        break

      default:
        errors.push({
          path: pathPrefix,
          message: `Unsupported platform for credential validation: ${platform}`,
          code: ValidationErrorCode.INVALID_PLATFORM,
          value: platform,
        })
    }
  }

  /**
   * Validate Pacifica platform credentials
   */
  private validatePacificaCredentials(
    credentials: any,
    pathPrefix: string,
    errors: ValidationError[],
    warnings: ValidationWarning[],
  ): void {
    if (credentials.type !== 'pacifica') {
      errors.push({
        path: `${pathPrefix}.type`,
        message: 'Pacifica credentials must have type "pacifica"',
        code: ValidationErrorCode.INVALID_FORMAT,
        value: credentials.type,
      })
    }

    if (!credentials.privateKey) {
      errors.push({
        path: `${pathPrefix}.privateKey`,
        message: 'Pacifica private key is required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    } else if (typeof credentials.privateKey !== 'string') {
      errors.push({
        path: `${pathPrefix}.privateKey`,
        message: 'Pacifica private key must be a string',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof credentials.privateKey,
      })
    } else if (!PACIFICA_PRIVATE_KEY_PATTERN.test(credentials.privateKey)) {
      errors.push({
        path: `${pathPrefix}.privateKey`,
        message: 'Pacifica private key must be 64 hexadecimal characters',
        code: ValidationErrorCode.INVALID_FORMAT,
        value: `${credentials.privateKey.length} characters`,
      })
    }
  }

  /**
   * Validate Aster platform credentials
   */
  private validateAsterCredentials(
    credentials: any,
    pathPrefix: string,
    errors: ValidationError[],
    warnings: ValidationWarning[],
  ): void {
    if (credentials.type !== 'aster') {
      errors.push({
        path: `${pathPrefix}.type`,
        message: 'Aster credentials must have type "aster"',
        code: ValidationErrorCode.INVALID_FORMAT,
        value: credentials.type,
      })
    }

    if (!credentials.privateKey) {
      errors.push({
        path: `${pathPrefix}.privateKey`,
        message: 'Aster private key is required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    } else if (typeof credentials.privateKey !== 'string') {
      errors.push({
        path: `${pathPrefix}.privateKey`,
        message: 'Aster private key must be a string',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof credentials.privateKey,
      })
    } else if (!ASTER_PRIVATE_KEY_PATTERN.test(credentials.privateKey)) {
      errors.push({
        path: `${pathPrefix}.privateKey`,
        message: 'Aster private key must be 66 characters starting with "0x" followed by 64 hexadecimal characters',
        code: ValidationErrorCode.INVALID_FORMAT,
        value: `${credentials.privateKey.length} characters`,
      })
    }
  }

  /**
   * Validate Binance platform credentials
   */
  private validateBinanceCredentials(
    credentials: any,
    pathPrefix: string,
    errors: ValidationError[],
    warnings: ValidationWarning[],
  ): void {
    if (credentials.type !== 'binance') {
      errors.push({
        path: `${pathPrefix}.type`,
        message: 'Binance credentials must have type "binance"',
        code: ValidationErrorCode.INVALID_FORMAT,
        value: credentials.type,
      })
    }

    if (!credentials.apiKey) {
      errors.push({
        path: `${pathPrefix}.apiKey`,
        message: 'Binance API key is required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    } else if (typeof credentials.apiKey !== 'string') {
      errors.push({
        path: `${pathPrefix}.apiKey`,
        message: 'Binance API key must be a string',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof credentials.apiKey,
      })
    } else if (!BINANCE_API_KEY_PATTERN.test(credentials.apiKey)) {
      errors.push({
        path: `${pathPrefix}.apiKey`,
        message: 'Binance API key must be 64 alphanumeric characters',
        code: ValidationErrorCode.INVALID_FORMAT,
        value: `${credentials.apiKey.length} characters`,
      })
    }

    if (!credentials.secretKey) {
      errors.push({
        path: `${pathPrefix}.secretKey`,
        message: 'Binance secret key is required',
        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,
      })
    } else if (typeof credentials.secretKey !== 'string') {
      errors.push({
        path: `${pathPrefix}.secretKey`,
        message: 'Binance secret key must be a string',
        code: ValidationErrorCode.INVALID_TYPE,
        value: typeof credentials.secretKey,
      })
    } else if (credentials.secretKey.length === 0) {
      errors.push({
        path: `${pathPrefix}.secretKey`,
        message: 'Binance secret key cannot be empty',
        code: ValidationErrorCode.INVALID_FORMAT,
        value: credentials.secretKey,
      })
    }
  }

  /**
   * Normalize configuration data
   */
  private normalizeConfig(config: any): ConfigFile {
    const normalized: ConfigFile = {
      version: config.version,
      accounts: [],
    }

    if (Array.isArray(config.accounts)) {
      normalized.accounts = config.accounts.map((account: any) => ({
        id: account.id,
        platform: account.platform,
        credentials: account.credentials,
        metadata: account.metadata || {},
      }))
    }

    return normalized
  }
}

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

/**
 * Create a validator with default options
 */
export function createValidator(options?: ValidatorOptions): ConfigValidator {
  return new ConfigValidator(options)
}

/**
 * Create a strict validator for production use
 */
export function createStrictValidator(): ConfigValidator {
  return new ConfigValidator({
    strictMode: true,
    allowUnknownFields: false,
    normalizeData: true,
    validateCredentials: true,
    maxAccounts: 500,
  })
}

/**
 * Create a lenient validator for development
 */
export function createLenientValidator(): ConfigValidator {
  return new ConfigValidator({
    strictMode: false,
    allowUnknownFields: true,
    normalizeData: true,
    validateCredentials: false,
    maxAccounts: 1000,
  })
}

/**
 * Quick validation function
 */
export function validateConfig(config: any, options?: ValidatorOptions): ValidationResult {
  const validator = new ConfigValidator(options)
  return validator.validateConfig(config)
}

/**
 * Quick validation check (boolean only)
 */
export function isValidConfig(config: any, options?: ValidatorOptions): boolean {
  const validator = new ConfigValidator(options)
  return validator.isValid(config)
}

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

export default ConfigValidator
