/**
 * MEXC Futures API client.
 * Auth: ApiKey + Request-Time + Signature headers.
 * Signature = HMAC-SHA256(accessKey + timestamp + paramString, secretKey)
 * Base URL: https://api.mexc.com
 */
import crypto from 'node:crypto'
import https from 'node:https'

const BASE = 'https://api.mexc.com'

export interface MexcOrderResult {
  orderId: string
  filled: boolean
  avgPrice: number | null
  qty: number
  side: 'BUY' | 'SELL'
  elapsedMs: number
  attempts: number
}

export interface MexcPosition {
  symbol: string
  side: 'LONG' | 'SHORT'
  vol: number         // contracts
  openAvgPrice: number
  unrealizedPnl: number
  leverage: number
}

export interface MexcAsset {
  currency: string
  availableBalance: number
  equity: number
  positionMargin: number
  cashBalance: number
}

function hmac(secret: string, msg: string): string {
  return crypto.createHmac('sha256', secret).update(msg).digest('hex')
}

async function request(
  key: string, secret: string,
  method: 'GET' | 'POST' | 'DELETE',
  path: string,
  params: Record<string, unknown> = {}
): Promise<any> {
  const ts = Date.now().toString()
  let paramStr = ''
  if (method === 'GET' || method === 'DELETE') {
    const keys = Object.keys(params).filter(k => params[k] != null).sort()
    paramStr = keys.map(k => `${k}=${params[k]}`).join('&')
  } else {
    paramStr = Object.keys(params).length ? JSON.stringify(params) : ''
  }
  const sig = hmac(secret, key + ts + paramStr)
  const url = BASE + path + (paramStr && method !== 'POST' ? '?' + paramStr : '')
  const headers: Record<string, string> = {
    'ApiKey': key,
    'Request-Time': ts,
    'Signature': sig,
    'Content-Type': 'application/json',
    'User-Agent': 'txocap/1.0'
  }
  return new Promise((resolve, reject) => {
    const req = https.request(url, { method, headers }, res => {
      let d = ''
      res.on('data', c => d += c)
      res.on('end', () => { try { resolve(JSON.parse(d)) } catch { resolve({ _raw: d.slice(0, 300) }) } })
    })
    req.setTimeout(10000, () => { req.destroy(); reject(new Error('timeout')) })
    req.on('error', reject)
    if (method === 'POST' && paramStr) req.write(paramStr)
    req.end()
  })
}

export class MexcExecutor {
  constructor(
    private readonly key: string,
    private readonly secret: string
  ) {}

  private req(method: 'GET'|'POST'|'DELETE', path: string, params?: Record<string,unknown>) {
    return request(this.key, this.secret, method, path, params)
  }

  /** Get USDT futures balance */
  async getBalance(): Promise<number> {
    const r = await this.req('GET', '/api/v1/private/account/assets')
    const usdt = r?.data?.find((a: MexcAsset) => a.currency === 'USDT')
    return parseFloat(usdt?.availableBalance ?? '0') || 0
  }

  /** Get equity (available + position margin) */
  async getEquity(): Promise<number> {
    const r = await this.req('GET', '/api/v1/private/account/assets')
    const usdt = r?.data?.find((a: MexcAsset) => a.currency === 'USDT')
    return parseFloat(usdt?.equity ?? '0') || 0
  }

  /** Get open position for BTC_USDT */
  async getPosition(symbol = 'BTC_USDT'): Promise<MexcPosition | null> {
    const r = await this.req('GET', '/api/v1/private/position/open_positions', { symbol })
    const pos = r?.data?.[0]
    if (!pos || pos.vol === 0) return null
    return {
      symbol: pos.symbol,
      side: pos.positionType === 1 ? 'LONG' : 'SHORT',
      vol: pos.vol,
      openAvgPrice: pos.openAvgPrice,
      unrealizedPnl: pos.unrealized,
      leverage: pos.leverage
    }
  }

  /** Set leverage */
  async setLeverage(symbol: string, leverage: number): Promise<boolean> {
    // MEXC: set leverage via open position endpoint
    const r = await this.req('POST', '/api/v1/private/position/change_leverage', {
      symbol, leverage, openType: 1, positionType: 1  // isolated, long
    })
    const r2 = await this.req('POST', '/api/v1/private/position/change_leverage', {
      symbol, leverage, openType: 1, positionType: 2  // isolated, short
    })
    return r?.success || r2?.success || false
  }

  /** Place a limit order */
  async placeLimitOrder(
    symbol: string,
    side: 'BUY' | 'SELL',
    vol: number,       // contracts (0.0001 BTC each)
    price: number,
    reduceOnly = false
  ): Promise<string | null> {
    const r = await this.req('POST', '/api/v1/private/order/submit', {
      symbol,
      price: price.toFixed(1),
      vol,
      side: side === 'BUY' ? 1 : 2,  // 1=Open Long or Close Short, 2=Open Short or Close Long
      type: 1,    // limit
      openType: 1, // isolated
      positionType: side === 'BUY' ? 1 : 2,  // 1=long, 2=short
      reduceOnly: reduceOnly ? true : undefined,
      externalOid: `txocap_${Date.now()}`
    })
    return r?.success ? r.data : null
  }

  /** Place a market order (close position) */
  async placeMarketOrder(
    symbol: string,
    side: 'BUY' | 'SELL',
    vol: number,
    reduceOnly = true
  ): Promise<string | null> {
    const r = await this.req('POST', '/api/v1/private/order/submit', {
      symbol,
      vol,
      side: side === 'BUY' ? 1 : 2,
      type: 5,    // market
      openType: 1,
      positionType: side === 'BUY' ? 1 : 2,
      reduceOnly: reduceOnly ? true : undefined
    })
    return r?.success ? r.data : null
  }

  /** Check if an order is filled */
  async getOrderStatus(symbol: string, orderId: string): Promise<{ filled: boolean; avgPrice: number | null; vol: number }> {
    const r = await this.req('GET', '/api/v1/private/order/get_order_detail', { symbol, orderId })
    const o = r?.data
    if (!o) return { filled: false, avgPrice: null, vol: 0 }
    // status: 1=pending, 2=filled, 3=partially filled, 4=cancelled
    const filled = o.state === 2 || (o.state === 3 && o.dealVol >= o.vol)
    return { filled, avgPrice: filled ? parseFloat(o.dealAvgPrice || o.price) : null, vol: o.dealVol || 0 }
  }

  /** Cancel an order */
  async cancelOrder(symbol: string, orderId: string): Promise<boolean> {
    const r = await this.req('DELETE', '/api/v1/private/order/cancel', { symbol, orderId })
    return r?.success || false
  }

  /** Cancel all open orders for symbol */
  async cancelAll(symbol: string): Promise<boolean> {
    const r = await this.req('DELETE', '/api/v1/private/order/cancel_all', { symbol })
    return r?.success || false
  }

  /**
   * Chase limit order: place limit, reprice up to maxAttempts times if unfilled.
   * For maker execution: place at mid, wait waitMs, reprice if unfilled.
   */
  async chaseLimitOrder(
    symbol: string,
    side: 'BUY' | 'SELL',
    vol: number,
    getPriceFn: () => number,
    maxAttempts = 3,
    waitMs = 4000
  ): Promise<MexcOrderResult> {
    const start = Date.now()
    let attempts = 0
    let orderId: string | null = null

    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      attempts++
      const price = getPriceFn()
      if (!price) break

      // Cancel previous order if any
      if (orderId) {
        await this.cancelOrder(symbol, orderId).catch(() => {})
        orderId = null
        await new Promise(r => setTimeout(r, 200))
      }

      orderId = await this.placeLimitOrder(symbol, side, vol, price)
      if (!orderId) continue

      // Wait for fill
      const deadline = Date.now() + waitMs
      while (Date.now() < deadline) {
        await new Promise(r => setTimeout(r, 500))
        const status = await this.getOrderStatus(symbol, orderId)
        if (status.filled) {
          return { orderId, filled: true, avgPrice: status.avgPrice, qty: status.vol, side, elapsedMs: Date.now() - start, attempts }
        }
      }
    }

    // Final cancel if still open
    if (orderId) await this.cancelOrder(symbol, orderId).catch(() => {})
    return { orderId: orderId ?? '', filled: false, avgPrice: null, qty: 0, side, elapsedMs: Date.now() - start, attempts }
  }

  /** Get current BTC mark price */
  async getMarkPrice(symbol = 'BTC_USDT'): Promise<number> {
    const r = await this.req('GET', '/api/v1/contract/detail', { symbol })
    return parseFloat(r?.data?.indexPrice ?? '0') || 0
  }

  /** Transfer USDT from spot to futures */
  async transferToFutures(amount: number): Promise<boolean> {
    const r = await this.req('POST', '/api/v1/private/assets/internal_transfer_futures', {
      currency: 'USDT',
      amount: amount.toString(),
      positionType: 1  // futures
    })
    return r?.success || false
  }
}
