import { EventEmitter } from 'events'
import { ExchangeAdapter, PlaceOrderReq, Order, Position, Balance, Depth } from '../ExchangeAdapter'
import { AsterAdapter } from './asterAdapter'
import {
  normalizeAsterAccountInfoEvent,
  normalizeAsterBalanceEvent,
  normalizeAsterOrdersEvent,
  normalizeAsterPositionsEvent,
} from '../unifiedEvents'
import { AsterWsClient } from './wsClient'
import { httpClient } from '../../utils/httpClient.js'

export class AsterExchangeAdapter implements ExchangeAdapter {
  private events = new EventEmitter()
  private userWs?: AsterWsClient
  private userKeepAliveTimer?: NodeJS.Timeout
  constructor(private client: AsterAdapter) {}

  name() {
    return 'aster'
  }
  async time(): Promise<number> {
    return Date.now()
  }
  async symbols(): Promise<string[]> {
    try {
      const base = (this.client as any).cfg?.httpBase || process.env.ASTER_HTTP_BASE || ''
      const res = await httpClient.get(`${String(base).replace(/\/$/, '')}/fapi/v3/exchangeInfo`, {
        exchange: 'aster',
        timeout: 10000,
      })

      if (!res.ok) {
        return []
      }

      return (Array.isArray(res.data?.symbols) ? res.data.symbols : [])
        .map((s: any) => String(s.symbol))
        .filter(Boolean)
    } catch {
      return []
    }
  }
  async balances(): Promise<Balance[]> {
    try {
      const arr = await (this.client as any).getBalances()
      return (Array.isArray(arr) ? arr : []).map((b: any) => ({
        asset: String(b.asset ?? b.balanceAsset ?? 'USDT'),
        total: String(b.balance ?? b.balanceTotal ?? b.walletBalance ?? '0'),
        free: String(b.availableBalance ?? b.free ?? '0'),
      }))
    } catch {
      return []
    }
  }
  async positions(): Promise<Position[]> {
    try {
      const arr = await (this.client as any).getPositionRisk()
      return (Array.isArray(arr) ? arr : [])
        .map((p: any) => ({
          symbol: String(p.symbol ?? ''),
          side: (Number(p.positionAmt ?? p.positionAmtAbs ?? 0) >= 0 ? 'LONG' : 'SHORT') as 'LONG' | 'SHORT',
          qty: String(p.positionAmt ?? '0'),
          entryPrice: String(p.entryPrice ?? '0'),
          unrealizedPnl: String(p.unRealizedProfit ?? p.unrealizedProfit ?? '0'),
          leverage: Number(p.leverage ?? 0),
        }))
        .filter(x => x.symbol)
    } catch {
      return []
    }
  }
  async placeOrder(req: PlaceOrderReq): Promise<Order> {
    const isMarket = req.type === 'MARKET'
    const side = req.side === 'BUY' ? 'long' : 'short'
    const price = req.price ? String(req.price) : undefined
    const res = await this.client.openPerp({
      symbol: req.symbol,
      side: side as any,
      quantity: Number(req.quantity),
      slippage: 0,
      deadlineSec: Math.floor(Date.now() / 1000) + 60,
      positionSide: undefined,
      ...(isMarket ? {} : { type: 'LIMIT' as any, price }),
    })
    return {
      id: String(
        res?.txHash
          ? (() => {
              try {
                return JSON.parse(String(res.txHash)).orderId
              } catch {
                return Date.now()
              }
            })()
          : Date.now(),
      ),
      symbol: req.symbol,
      status: res.success ? 'NEW' : 'REJECTED',
      side: req.side,
      type: req.type,
      price: price,
      origQty: String(req.quantity),
      executedQty: '0',
      updateTime: Date.now(),
    }
  }
  async cancelOrder(symbol: string, orderId: string): Promise<void> {
    await this.client.cancelOrderRest({ symbol, side: 'BUY', type: 'LIMIT', orderId: Number(orderId) })
  }
  async cancelAll(symbol: string): Promise<void> {
    // Aster 无单一 cancelAll 端点，这里可拉取 openOrders 后逐个撤单
    const opens = await this.openOrders(symbol)
    for (const o of opens) {
      try {
        await this.cancelOrder(symbol, o.id)
      } catch {}
    }
  }
  async getOrder(symbol: string, orderId: string): Promise<Order> {
    const info = await this.client.getOrderRest({ symbol, side: 'BUY', type: 'LIMIT', orderId: Number(orderId) })
    return {
      id: String(orderId),
      symbol,
      status: this.mapOrderStatus(info?.status),
      side: this.mapOrderSide(info?.side),
      type: this.mapOrderType(info?.type),
      price: String(info?.price ?? '0'),
      origQty: String(info?.origQty ?? '0'),
      executedQty: String(info?.executedQty ?? '0'),
      updateTime: Number(info?.updateTime ?? Date.now()),
    }
  }
  async openOrders(symbol: string): Promise<Order[]> {
    try {
      const arr = await (this.client as any).getOpenOrders(symbol)
      return (Array.isArray(arr) ? arr : []).map((o: any) => ({
        id: String(o.orderId ?? o.clientOrderId ?? Date.now()),
        symbol: String(o.symbol ?? symbol),
        status: this.mapOrderStatus(o?.status),
        side: this.mapOrderSide(o?.side),
        type: this.mapOrderType(o?.type),
        price: String(o.price ?? '0'),
        origQty: String(o.origQty ?? o.quantity ?? '0'),
        executedQty: String(o.executedQty ?? o.cumQty ?? '0'),
        updateTime: Number(o.updateTime ?? Date.now()),
      }))
    } catch {
      return []
    }
  }
  async leverage(symbol: string, lev: number): Promise<void> {
    await this.client.updateLeverage(symbol, lev)
  }
  async depth(symbol: string, _limit = 50): Promise<Depth> {
    const base = (this.client as any).cfg?.httpBase || process.env.ASTER_HTTP_BASE || ''
    const res = await (globalThis.fetch as any)(
      `${String(base).replace(/\/$/, '')}/fapi/v3/depth?symbol=${encodeURIComponent(symbol)}&limit=20`,
    )
    const data = await (res as any).json()
    const bids = (data?.bids ?? []).map((x: any) => ({ price: String(x[0]), qty: String(x[1]) }))
    const asks = (data?.asks ?? []).map((x: any) => ({ price: String(x[0]), qty: String(x[1]) }))
    return { bids, asks, ts: Date.now() }
  }
  ws() {
    return this.events
  }

  /** 启动账户 WS（listenKey）。需要在 cfg 中提供 apiKey/apiSecret 与 httpBase。*/
  async startAccountUserStream(wsBase = 'wss://fstream.asterdex.com') {
    // 1) 创建/续期 listenKey
    const ensured = await this.client.ensureListenKey()
    const lk = ensured.listenKey
    // 2) 连接 user stream
    const c = new AsterWsClient({ wsUrl: wsBase })
    c.setUserStream(lk, wsBase)
    c.on('raw', (m: any) => this.events.emit('raw', m))
    c.on('balance', (p: any) => this.events.emit('balance', normalizeAsterBalanceEvent(p)))
    c.on('account_positions', (p: any) => this.events.emit('account_positions', normalizeAsterPositionsEvent(p)))
    c.on('account_info', (p: any) => this.events.emit('account_info', normalizeAsterAccountInfoEvent(p)))
    c.on('orders', (p: any) => this.events.emit('orders', normalizeAsterOrdersEvent(p)))
    c.on('error', (e: any) => this.events.emit('ws_error', e))
    c.on('ws_error', async (e: any) => {
      this.events.emit('ws_error', e)
      const code = e?.code
      if (code === 'listenKeyExpired' || code === 'INVALID_LISTEN_KEY' || code === -1125) {
        // 重新创建 listenKey 并重连
        try {
          const newState = await this.client.ensureListenKey({ forceNew: true })
          const newKey = newState.listenKey
          c.setUserStream(newKey, wsBase)
          c.connectUserStream()
        } catch {}
      }
    })
    c.on('close', () => this.events.emit('ws_close', {}))
    c.connectUserStream()
    this.userWs = c
    // 3) 定时 keepAlive（官方 60 分钟；这里 30 分钟）
    if (this.userKeepAliveTimer) {
      clearInterval(this.userKeepAliveTimer)
    }
    this.userKeepAliveTimer = setInterval(
      () => {
        this.client.ensureListenKey({ refreshThresholdMs: 0 }).catch(() => {})
      },
      30 * 60 * 1000,
    )
  }

  stopAccountUserStream() {
    if (this.userWs) {
      try {
        this.userWs.disconnect()
      } catch {}
      this.userWs = undefined
    }
    if (this.userKeepAliveTimer) {
      clearInterval(this.userKeepAliveTimer)
      this.userKeepAliveTimer = undefined
    }
    // 可选：关闭 listenKey
    this.client.closeListenKey().catch(() => {})
  }

  // ===== Helpers: type-safe unions for Order fields =====
  private mapOrderStatus(input: any): Order['status'] {
    const u = String(input ?? 'NEW').toUpperCase()
    switch (u) {
      case 'NEW':
      case 'PARTIALLY_FILLED':
      case 'FILLED':
      case 'CANCELED':
      case 'REJECTED':
      case 'EXPIRED':
        return u as Order['status']
      default:
        return 'NEW'
    }
  }

  private mapOrderSide(input: any): 'BUY' | 'SELL' {
    const u = String(input ?? 'BUY').toUpperCase()
    return u === 'SELL' ? 'SELL' : 'BUY'
  }

  private mapOrderType(input: any): 'MARKET' | 'LIMIT' | 'POST_ONLY' {
    const u = String(input ?? 'LIMIT').toUpperCase()
    if (u === 'MARKET') return 'MARKET'
    if (u === 'POST_ONLY') return 'POST_ONLY'
    return 'LIMIT'
  }
}
