/**
 * Binance Platform Signer Implementation
 *
 * Implements HMAC-SHA256 signing for Binance API requests
 * according to Binance API authentication requirements.
 */

import { createHmac } from 'crypto'

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

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

export interface BinanceSignRequest {
  accountId: string
  method: 'GET' | 'POST' | 'PUT' | 'DELETE'
  endpoint: string
  params?: Record<string, any>
  options?: BinanceSignOptions
}

export interface BinanceSignOptions {
  timeout?: number
  includeTimestamp?: boolean
  recvWindow?: number
}

export interface BinanceSignResponse {
  success: boolean
  signature: string
  algorithm: 'hmac-sha256'
  apiKey: string
  timestamp: number
  queryString: string
  error?: string
}

export interface BinanceVerifyRequest {
  message: string
  signature: string
  secretKey: string
  algorithm?: 'hmac-sha256'
}

export interface BinanceVerifyResponse {
  valid: boolean
  algorithm: 'hmac-sha256'
  timestamp: Date
}

// ============================================================================
// Binance Signer Implementation
// ============================================================================

export class BinanceSigner {
  readonly platform = Platform.BINANCE

  private accounts = new Map<string, { credentials: BinanceCredentials }>()
  private signMetrics = {
    totalSigns: 0,
    successfulSigns: 0,
    failedSigns: 0,
    totalTime: 0,
  }

  constructor() {
    // Initialize with empty accounts map
  }

  /**
   * Register an account with its credentials
   */
  registerAccount(accountId: string, credentials: BinanceCredentials): void {
    if (!this.validateCredentials(credentials)) {
      throw new CredentialManagerError(
        `Invalid Binance credentials for account ${accountId}`,
        ErrorType.VALIDATION_ERROR,
      )
    }

    this.accounts.set(accountId, { credentials })
  }

  /**
   * Remove an account
   */
  removeAccount(accountId: string): boolean {
    return this.accounts.delete(accountId)
  }

  /**
   * Sign Binance API request with HMAC-SHA256
   */
  async signRequest(request: BinanceSignRequest): Promise<BinanceSignResponse> {
    const startTime = Date.now()
    this.signMetrics.totalSigns++

    try {
      const account = this.accounts.get(request.accountId)
      if (!account) {
        throw new CredentialManagerError(`Account ${request.accountId} not found`, ErrorType.SIGNATURE_ERROR)
      }

      const { credentials } = account
      const timestamp = Date.now()
      const options = request.options || {}

      // Build query string with parameters
      const allParams = {
        ...request.params,
        ...(options.includeTimestamp !== false && { timestamp }),
        ...(options.recvWindow && { recvWindow: options.recvWindow }),
      }

      const queryString = this.buildQueryString(allParams)

      // Generate HMAC-SHA256 signature
      const signature = this.computeHmacSha256(queryString, credentials.secretKey)

      const duration = Date.now() - startTime
      this.signMetrics.successfulSigns++
      this.signMetrics.totalTime += duration

      return {
        success: true,
        signature,
        algorithm: 'hmac-sha256',
        apiKey: credentials.apiKey,
        timestamp,
        queryString,
      }
    } catch (error) {
      const duration = Date.now() - startTime
      this.signMetrics.failedSigns++
      this.signMetrics.totalTime += duration

      return {
        success: false,
        signature: '',
        algorithm: 'hmac-sha256',
        apiKey: '',
        timestamp: Date.now(),
        queryString: '',
        error: error instanceof Error ? error.message : 'Unknown signing error',
      }
    }
  }

  /**
   * Verify HMAC-SHA256 signature
   */
  async verifySignature(request: BinanceVerifyRequest): Promise<BinanceVerifyResponse> {
    try {
      const expected = this.computeHmacSha256(request.message, request.secretKey)
      const valid = this.safeStringCompare(expected, request.signature)

      return {
        valid,
        algorithm: 'hmac-sha256',
        timestamp: new Date(),
      }
    } catch (error) {
      return {
        valid: false,
        algorithm: 'hmac-sha256',
        timestamp: new Date(),
      }
    }
  }

  /**
   * Get API key for account
   */
  async getApiKey(accountId: string): Promise<string> {
    const account = this.accounts.get(accountId)
    if (!account) {
      throw new CredentialManagerError(`Account ${accountId} not found`, ErrorType.SIGNATURE_ERROR)
    }

    return account.credentials.apiKey
  }

  /**
   * Sign multiple requests in batch
   */
  async signBatch(requests: BinanceSignRequest[]): Promise<BinanceSignResponse[]> {
    // Process requests in parallel for better performance
    const promises = requests.map(request => this.signRequest(request))
    return Promise.all(promises)
  }

  /**
   * Get signing metrics for monitoring
   */
  getMetrics() {
    const avgTime = this.signMetrics.totalSigns > 0 ? this.signMetrics.totalTime / this.signMetrics.totalSigns : 0

    return {
      totalSigns: this.signMetrics.totalSigns,
      successfulSigns: this.signMetrics.successfulSigns,
      failedSigns: this.signMetrics.failedSigns,
      successRate: this.signMetrics.totalSigns > 0 ? this.signMetrics.successfulSigns / this.signMetrics.totalSigns : 0,
      averageSignTime: avgTime,
    }
  }

  /**
   * Reset metrics
   */
  resetMetrics(): void {
    this.signMetrics = {
      totalSigns: 0,
      successfulSigns: 0,
      failedSigns: 0,
      totalTime: 0,
    }
  }

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

  /**
   * Validate Binance credentials format
   */
  private validateCredentials(credentials: BinanceCredentials): boolean {
    return (
      credentials.type === 'binance' &&
      typeof credentials.apiKey === 'string' &&
      credentials.apiKey.length > 0 &&
      typeof credentials.secretKey === 'string' &&
      credentials.secretKey.length > 0
    )
  }

  /**
   * Build query string from parameters (sorted by key)
   */
  private buildQueryString(params: Record<string, any>): string {
    const sortedKeys = Object.keys(params).sort()
    return sortedKeys.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`).join('&')
  }

  /**
   * Compute HMAC-SHA256 signature
   */
  private computeHmacSha256(message: string, secret: string): string {
    return createHmac('sha256', secret).update(message).digest('hex')
  }

  /**
   * Safe string comparison to prevent timing attacks
   */
  private safeStringCompare(a: string, b: string): boolean {
    if (a.length !== b.length) {
      return false
    }

    let result = 0
    for (let i = 0; i < a.length; i++) {
      result |= a.charCodeAt(i) ^ b.charCodeAt(i)
    }

    return result === 0
  }
}

// ============================================================================
// Binance API Constants
// ============================================================================

export const BINANCE_CONSTANTS = {
  /**
   * Default receive window (milliseconds)
   */
  DEFAULT_RECV_WINDOW: 5000,

  /**
   * Maximum receive window (milliseconds)
   */
  MAX_RECV_WINDOW: 60000,

  /**
   * Default request timeout (milliseconds)
   */
  DEFAULT_TIMEOUT: 30000,

  /**
   * Maximum batch size for concurrent requests
   */
  MAX_BATCH_SIZE: 100,

  /**
   * Endpoints that require signatures
   */
  SIGNED_ENDPOINTS: [
    '/api/v3/account',
    '/api/v3/order',
    '/api/v3/allOrders',
    '/api/v3/openOrders',
    '/api/v3/myTrades',
    '/fapi/v1/account',
    '/fapi/v1/balance',
    '/fapi/v1/positionRisk',
  ],

  /**
   * Endpoints that don't require signatures
   */
  PUBLIC_ENDPOINTS: [
    '/api/v3/ping',
    '/api/v3/time',
    '/api/v3/exchangeInfo',
    '/api/v3/depth',
    '/api/v3/trades',
    '/api/v3/ticker/price',
    '/api/v3/ticker/24hr',
  ],
} as const

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

/**
 * Check if endpoint requires signature
 */
export function requiresSignature(endpoint: string): boolean {
  return BINANCE_CONSTANTS.SIGNED_ENDPOINTS.some(pattern => endpoint.startsWith(pattern))
}

/**
 * Validate Binance API key format
 */
export function validateApiKeyFormat(apiKey: string): boolean {
  // Binance API keys are typically 64 characters long, alphanumeric
  return /^[A-Za-z0-9]{64}$/.test(apiKey)
}

/**
 * Validate Binance secret key format
 */
export function validateSecretKeyFormat(secretKey: string): boolean {
  // Binance secret keys are typically 64 characters long, alphanumeric
  return /^[A-Za-z0-9]{64}$/.test(secretKey)
}

/**
 * Build complete request URL with query parameters
 */
export function buildRequestUrl(baseUrl: string, endpoint: string, queryString: string): string {
  const separator = queryString ? '?' : ''
  return `${baseUrl}${endpoint}${separator}${queryString}`
}

/**
 * Extract parameters from query string
 */
export function parseQueryString(queryString: string): Record<string, string> {
  const params: Record<string, string> = {}

  if (!queryString) {
    return params
  }

  const pairs = queryString.split('&')
  for (const pair of pairs) {
    const [key, value] = pair.split('=')
    if (key && value) {
      params[decodeURIComponent(key)] = decodeURIComponent(value)
    }
  }

  return params
}

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

export default BinanceSigner
