import { AbiCoder, Wallet, keccak256, getBytes } from 'ethers'
import { createHmac } from 'crypto'

export interface AsterConfig {
  rpcUrl: string
  chainId: number
  routerAddress: string // Aster 合约/路由地址（根据文档填写）
  readerAddress?: string // 读取器合约地址（可选）
  httpBase?: string // REST API Base，例如 https://fapi.asterdex.com
  defaultUser?: string // 下单 user 地址（owner）
  defaultSigner?: string // 下单 signer 地址（与私钥对应）
  apiKey?: string // （可选）REST HMAC API Key，用于读取账户设置
  apiSecret?: string // （可选）REST HMAC API Secret
}

export interface AsterQuoteParams {
  symbol: string // 交易对（与 Aster 平台定义保持一致）
  side: 'long' | 'short'
  quantity: number // 张数或标的数量（依据 Aster 定义）
  slippage: number // 最大滑点比例
}

export interface AsterQuoteResult {
  expectedPrice: number
  feeUsd?: number
  fundingRate?: number
}

export interface AsterOrderParams extends AsterQuoteParams {
  deadlineSec: number
  positionSide?: 'BOTH' | 'LONG' | 'SHORT'
}

export interface AsterOrderResult {
  success: boolean
  txHash?: string
  error?: string
}

export class AsterAdapter {
  private provider: any // 懒加载链上需求
  private signer?: Wallet
  private cfg: AsterConfig
  private dualSideCache?: boolean
  private listenKeyState?: { key: string; updatedAt: number }

  constructor(cfg: AsterConfig, signer?: Wallet) {
    this.cfg = cfg
    // 仅在需要链上时使用 provider
    // @ts-ignore
    this.provider = undefined
    this.signer = signer
  }

  async getFundingRate(symbol: string): Promise<number | null> {
    // 使用 REST: GET /fapi/v3/fundingRate?symbol=BTCUSDT&limit=1 取最近一条
    const base = this.cfg.httpBase || process.env.ASTER_HTTP_BASE || ''
    if (!base) return null
    const url = `${base.replace(/\/$/, '')}/fapi/v3/fundingRate?symbol=${encodeURIComponent(symbol)}&limit=1`
    try {
      const res = await (globalThis.fetch as any)(url, { method: 'GET' } as any)
      if (!('ok' in res) || !(res as any).ok) return null
      const data = await (res as any).json()
      const item = Array.isArray(data) ? data[0] : data
      const rate = Number(item?.fundingRate ?? item?.funding_rate ?? item?.r)
      return Number.isFinite(rate) ? rate : null
    } catch {
      return null
    }
  }

  async quote(params: AsterQuoteParams): Promise<AsterQuoteResult> {
    // 简化：用标记价格/最新价作为预估成交价，并返回当前资金费率
    const base = this.cfg.httpBase || process.env.ASTER_HTTP_BASE || ''
    if (!base) return { expectedPrice: 0 }
    const url = `${base.replace(/\/$/, '')}/fapi/v3/ticker/price?symbol=${encodeURIComponent(params.symbol)}`
    try {
      const res = await (globalThis.fetch as any)(url, { method: 'GET' } as any)
      const data = await (res as any).json()
      const px = Number(data?.price ?? data?.p ?? data?.lastPrice)
      const funding = await this.getFundingRate(params.symbol)
      return { expectedPrice: Number.isFinite(px) ? px : 0, fundingRate: funding ?? undefined }
    } catch {
      return { expectedPrice: 0 }
    }
  }

  async openPerp(params: AsterOrderParams): Promise<AsterOrderResult> {
    // 使用 REST 市价下单（BUY，LONG 或 BOTH 由调用方选择）
    const side = params.side === 'long' ? 'BUY' : 'SELL'
    try {
      const posSide = params.positionSide ?? (await this.choosePositionSide(side as 'BUY' | 'SELL'))
      const attempt = async (positionSide: 'BOTH' | 'LONG' | 'SHORT') => {
        const sig = await this.generateOrderSignature({
          symbol: params.symbol,
          positionSide,
          type: 'MARKET',
          side: side as any,
          quantity: params.quantity,
          timeInForce: undefined,
        })
        const url = `${this.requireHttpBase()}/fapi/v3/order`
        const body = new URLSearchParams(sig.formFields).toString()
        const res = await (globalThis.fetch as any)(url, {
          method: 'POST',
          headers: { 'content-type': 'application/x-www-form-urlencoded' } as any,
          body,
        } as any)
        const text = await res.text()
        return { ok: (res as any).ok, text }
      }
      const r1 = await attempt(posSide)
      if (r1.ok) return { success: true, txHash: r1.text }
      if (r1.text.includes('position side') || r1.text.includes('positionSide')) {
        // 切换模式重试：BOTH <-> LONG/SHORT
        const fallback: 'BOTH' | 'LONG' | 'SHORT' = posSide === 'BOTH' ? (side === 'BUY' ? 'LONG' : 'SHORT') : 'BOTH'
        const r2 = await attempt(fallback)
        if (r2.ok) return { success: true, txHash: r2.text }
        return { success: false, error: r2.text.slice(0, 200) }
      }
      return { success: false, error: r1.text.slice(0, 200) }
    } catch (e: any) {
      return { success: false, error: e?.message || String(e) }
    }
  }

  async closePerp(params: AsterOrderParams): Promise<AsterOrderResult> {
    const side = params.side === 'long' ? 'SELL' : 'BUY'
    try {
      const posSide = params.positionSide ?? (await this.choosePositionSide(side as 'BUY' | 'SELL'))
      const attempt = async (positionSide: 'BOTH' | 'LONG' | 'SHORT') => {
        const sig = await this.generateOrderSignature({
          symbol: params.symbol,
          positionSide,
          type: 'MARKET',
          side: side as any,
          quantity: params.quantity,
          timeInForce: undefined,
        })
        const url = `${this.requireHttpBase()}/fapi/v3/order`
        const body = new URLSearchParams(sig.formFields).toString()
        const res = await (globalThis.fetch as any)(url, {
          method: 'POST',
          headers: { 'content-type': 'application/x-www-form-urlencoded' } as any,
          body,
        } as any)
        const text = await res.text()
        return { ok: (res as any).ok, text }
      }
      const r1 = await attempt(posSide)
      if (r1.ok) return { success: true, txHash: r1.text }
      if (r1.text.includes('position side') || r1.text.includes('positionSide')) {
        const fallback: 'BOTH' | 'LONG' | 'SHORT' = posSide === 'BOTH' ? (side === 'BUY' ? 'LONG' : 'SHORT') : 'BOTH'
        const r2 = await attempt(fallback)
        if (r2.ok) return { success: true, txHash: r2.text }
        return { success: false, error: r2.text.slice(0, 200) }
      }
      return { success: false, error: r1.text.slice(0, 200) }
    } catch (e: any) {
      return { success: false, error: e?.message || String(e) }
    }
  }

  // === REST: 查单 / 撤单（使用与下单相同的签名流程） ===

  /** 生成撤单签名 */
  async generateCancelSignature(
    req: {
      symbol: string
      side: 'BUY' | 'SELL' | 'buy' | 'sell'
      type: 'LIMIT' | 'MARKET' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET'
      orderId?: number
      clientOrderId?: string
      recvWindow?: number
      timestamp?: number
    },
    creds?: { user?: string; signer?: string; privateKey?: string; nonceMicros?: bigint },
  ): Promise<{ formFields: Record<string, string> }> {
    const business: Record<string, any> = {
      ...req,
      recvWindow: req.recvWindow ?? 50000,
      timestamp: req.timestamp ?? Date.now(),
    }
    const bizStrDict = this.normalizeBusinessParams(business)
    const jsonStr = this.stableJSONString(bizStrDict)
    const user = creds?.user || this.cfg.defaultUser || process.env.ASTER_ORDER_USER || ''
    const signer = creds?.signer || this.cfg.defaultSigner || process.env.ASTER_ORDER_SIGNER || ''
    const privateKey = creds?.privateKey || process.env.PRIVATE_KEY || ''
    if (!user || !signer || !privateKey) throw new Error('缺少用户、签名者或私钥')
    const nonce = creds?.nonceMicros ?? BigInt(Math.trunc(Date.now() * 1000))
    const signature = await this.signOrderJSONString(jsonStr, user, signer, nonce, privateKey)
    const formFields: Record<string, string> = {
      ...bizStrDict,
      nonce: String(nonce),
      user,
      signer,
      signature,
    }
    return { formFields }
  }

  /** GET /fapi/v3/order 查单 */
  async getOrderRest(params: {
    symbol: string
    side: 'BUY' | 'SELL'
    type: string
    orderId?: number
    clientOrderId?: string
  }): Promise<any> {
    const base = this.requireHttpBase()
    const { formFields } = await this.generateCancelSignature(params)
    const qs = new URLSearchParams(formFields).toString()
    const url = `${base}/fapi/v3/order?${qs}`
    const res = await (globalThis.fetch as any)(url, { method: 'GET' } as any)
    const txt = await res.text()
    if (!('ok' in res) || !(res as any).ok)
      throw new Error(`HTTP ${(res as any).status} GET /fapi/v3/order - ${txt.slice(0, 200)}`)
    try {
      return JSON.parse(txt)
    } catch {
      return txt
    }
  }

  /** DELETE /fapi/v3/order 撤单 */
  async cancelOrderRest(params: {
    symbol: string
    side: 'BUY' | 'SELL'
    type: string
    orderId?: number
    clientOrderId?: string
  }): Promise<any> {
    const base = this.requireHttpBase()
    const { formFields } = await this.generateCancelSignature(params)
    const qs = new URLSearchParams(formFields).toString()
    const url = `${base}/fapi/v3/order?${qs}`
    const res = await (globalThis.fetch as any)(url, { method: 'DELETE' } as any)
    const txt = await res.text()
    if (!('ok' in res) || !(res as any).ok)
      throw new Error(`HTTP ${(res as any).status} DELETE /fapi/v3/order - ${txt.slice(0, 200)}`)
    try {
      return JSON.parse(txt)
    } catch {
      return txt
    }
  }

  // ===== 账户/杠杆相关 =====
  async getLeverageBracket(symbol?: string): Promise<any> {
    const base = this.requireHttpBase()
    const url = `${base}/fapi/v3/leverageBracket${symbol ? `?symbol=${encodeURIComponent(symbol)}` : ''}`
    const res = await (globalThis.fetch as any)(url, { method: 'GET' } as any)
    if (!('ok' in res) || !(res as any).ok) throw new Error(`HTTP ${(res as any).status} /fapi/v3/leverageBracket`)
    return await (res as any).json()
  }

  async updateLeverage(symbol: string, leverage: number): Promise<any> {
    // 需要 HMAC 鉴权
    const body = await this.hmacGet('/fapi/v3/leverage', { symbol, leverage })
    return body
  }

  async updateMarginMode(symbol: string, marginType: 'ISOLATED' | 'CROSSED'): Promise<any> {
    // 需要 HMAC 鉴权
    const body = await this.hmacGet('/fapi/v3/marginType', { symbol, marginType })
    return body
  }

  // === REST 下单封装（按照文档 /fapi/v3/order 签名流程） ===

  /**
   * 业务参数 -> 纯字符串字典（递归字符串化 list/dict），移除空值
   */
  private normalizeBusinessParams(input: Record<string, any>): Record<string, string> {
    const cleaned: Record<string, any> = {}
    for (const [k, v] of Object.entries(input)) {
      if (v === undefined || v === null) continue
      cleaned[k] = v
    }

    const toStringValue = (val: any): string => {
      if (Array.isArray(val)) {
        const arr = val.map(item =>
          typeof item === 'object' && item !== null ? JSON.stringify(this.deepStringify(item)) : String(item),
        )
        return JSON.stringify(arr)
      }
      if (typeof val === 'object') {
        return JSON.stringify(this.deepStringify(val))
      }
      return String(val)
    }

    const out: Record<string, string> = {}
    for (const [k, v] of Object.entries(cleaned)) {
      out[k] = toStringValue(v)
    }
    return out
  }

  private deepStringify(obj: Record<string, any>): Record<string, string> {
    const out: Record<string, string> = {}
    for (const [k, v] of Object.entries(obj)) {
      if (v === undefined || v === null) continue
      if (Array.isArray(v)) {
        out[k] = JSON.stringify(
          v.map(item => (typeof item === 'object' && item !== null ? this.deepStringify(item) : String(item))),
        )
      } else if (typeof v === 'object') {
        out[k] = JSON.stringify(this.deepStringify(v as any))
      } else {
        out[k] = String(v)
      }
    }
    return out
  }

  /**
   * 生成按 ASCII 排序的 JSON 字符串
   */
  private stableJSONString(obj: Record<string, string>): string {
    const entries = Object.entries(obj).sort(([a], [b]) => a.localeCompare(b))
    const sorted = Object.fromEntries(entries)
    return JSON.stringify(sorted)
  }

  /**
   * 计算签名（ABI 编码 ['string','address','address','uint256'] 后 keccak，再 personal_sign）
   */
  private async signOrderJSONString(
    jsonStr: string,
    user: string,
    signerAddr: string,
    nonce: bigint,
    privateKey: string,
  ): Promise<string> {
    const coder = AbiCoder.defaultAbiCoder()
    const encoded = coder.encode(['string', 'address', 'address', 'uint256'], [jsonStr, user, signerAddr, nonce])
    const hash = keccak256(encoded)
    const wallet = new Wallet(privateKey)
    const sig = await wallet.signMessage(getBytes(hash))
    return sig
  }

  /**
   * 仅生成签名（不发起 HTTP 请求）。
   * 返回 jsonStr（按 ASCII 排序的业务 JSON 字符串）、nonce（微秒）、signature、user、signer 供外部自行拼装表单提交。
   */
  async generateOrderSignature(
    order: {
      symbol: string
      positionSide: 'BOTH' | 'LONG' | 'SHORT'
      type: 'LIMIT' | 'MARKET' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET'
      side: 'BUY' | 'SELL'
      timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX' | 'GTD'
      quantity: string | number
      price?: string | number
      recvWindow?: number
      timestamp?: number
    },
    creds?: { user?: string; signer?: string; privateKey?: string; nonceMicros?: bigint },
  ): Promise<{
    jsonStr: string
    nonce: bigint
    signature: string
    user: string
    signer: string
    formFields: Record<string, string> // 直接用于 x-www-form-urlencoded 的字段
  }> {
    const business: Record<string, any> = {
      ...order,
      recvWindow: order.recvWindow ?? 50000,
      timestamp: order.timestamp ?? Date.now(),
    }

    const bizStrDict = this.normalizeBusinessParams(business)
    const jsonStr = this.stableJSONString(bizStrDict)

    const user = creds?.user || this.cfg.defaultUser || process.env.ASTER_ORDER_USER || ''
    const signer = creds?.signer || this.cfg.defaultSigner || process.env.ASTER_ORDER_SIGNER || ''
    const privateKey = creds?.privateKey || process.env.PRIVATE_KEY || ''
    if (!user || !signer || !privateKey) throw new Error('缺少用户、签名者或私钥')

    const nonce = creds?.nonceMicros ?? BigInt(Math.trunc(Date.now() * 1000))
    const signature = await this.signOrderJSONString(jsonStr, user, signer, nonce, privateKey)

    const formFields: Record<string, string> = {
      ...bizStrDict,
      nonce: String(nonce),
      user,
      signer,
      signature,
    }

    return { jsonStr, nonce, signature, user, signer, formFields }
  }

  // ===== REST/HMAC 工具与账户模式 =====
  private requireHttpBase(): string {
    const base = this.cfg.httpBase || process.env.ASTER_HTTP_BASE || ''
    if (!base) throw new Error('AsterAdapter: 需要httpBase')
    return base.replace(/\/$/, '')
  }

  private async hmacRequest(
    method: 'GET' | 'POST' | 'PUT' | 'DELETE',
    path: string,
    extra: Record<string, string | number> = {},
  ): Promise<any> {
    // 对于 listenKey 等端点，使用 Aster 的 ECDSA 签名算法
    const business: Record<string, any> = {
      ...extra,
      recvWindow: extra.recvWindow ?? 50000,
      timestamp: extra.timestamp ?? Date.now(),
    }

    const bizStrDict = this.normalizeBusinessParams(business)
    const jsonStr = this.stableJSONString(bizStrDict)

    const user = this.cfg.defaultUser || process.env.ASTER_ORDER_USER || ''
    const signer = this.cfg.defaultSigner || process.env.ASTER_API_KEY || ''
    const privateKey = process.env.ASTER_API_SECRET || ''
    if (!user || !signer || !privateKey) throw new Error('HMAC请求需要用户、签名者、私钥')

    const nonce = BigInt(Math.trunc(Date.now() * 1000))
    const signature = await this.signOrderJSONString(jsonStr, user, signer, nonce, privateKey)

    const formFields: Record<string, string> = {
      ...bizStrDict,
      nonce: String(nonce),
      user,
      signer,
      signature,
    }

    // Aster API 不需要 X-MBX-APIKEY header，所有认证通过签名参数
    const headers: Record<string, string> = {}
    let url = `${this.requireHttpBase()}${path}`
    const options: any = { method, headers }

    if (method === 'GET' || method === 'DELETE') {
      const qs = new URLSearchParams(formFields).toString()
      url += `?${qs}`
    } else {
      headers['content-type'] = 'application/x-www-form-urlencoded'
      options.body = new URLSearchParams(formFields).toString()
    }

    const res = await (globalThis.fetch as any)(url, options)
    const text = await res.text()
    if (!('ok' in res) || !(res as any).ok)
      throw new Error(`HTTP ${(res as any).status} ${path} - ${text.slice(0, 200)}`)
    try {
      return JSON.parse(text)
    } catch {
      return text
    }
  }

  private async hmacGet(path: string, extra: Record<string, string | number> = {}): Promise<any> {
    return await this.hmacRequest('GET', path, extra)
  }

  // === 公共账户查询（HMAC） ===
  async getBalances(): Promise<any[]> {
    return await this.hmacGet('/fapi/v3/balance')
  }

  async getPositionRisk(symbol?: string): Promise<any[]> {
    const params: Record<string, string | number> = {}
    if (symbol) params.symbol = symbol
    return await this.hmacGet('/fapi/v3/positionRisk', params)
  }

  async getOpenOrders(symbol?: string): Promise<any[]> {
    const params: Record<string, string | number> = {}
    if (symbol) params.symbol = symbol
    return await this.hmacGet('/fapi/v3/openOrders', params)
  }

  // ===== User Stream (listenKey) =====
  /** 创建 listenKey 用于账户类 WS。返回 listenKey 字符串。*/
  async createListenKey(): Promise<string> {
    // USER_STREAM 类型需要签名，尽管文档说参数为 None
    const body = await this.hmacRequest('POST', '/fapi/v3/listenKey', {})
    const lk = String(body?.listenKey || '')
    if (!lk) throw new Error('empty_listenKey')
    this.listenKeyState = { key: lk, updatedAt: Date.now() }
    return lk
  }

  /** 延长 listenKey 有效期（官方为 60 分钟有效期，这里建议每 30 分钟续期）。*/
  async keepAliveListenKey(): Promise<void> {
    if (!this.listenKeyState) throw new Error('listenKey_not_initialized')
    // USER_STREAM 类型需要签名
    await this.hmacRequest('PUT', '/fapi/v3/listenKey', {})
    this.listenKeyState.updatedAt = Date.now()
  }

  /** 关闭 listenKey（可选）。*/
  async closeListenKey(): Promise<void> {
    // USER_STREAM 类型需要签名
    await this.hmacRequest('DELETE', '/fapi/v3/listenKey', {})
    this.listenKeyState = undefined
  }

  async ensureListenKey(opts?: {
    forceNew?: boolean
    refreshThresholdMs?: number
  }): Promise<{ listenKey: string; refreshed: boolean; source: 'cached' | 'refreshed' | 'created' }> {
    const now = Date.now()
    const threshold = opts?.refreshThresholdMs ?? 45 * 60 * 1000
    if (opts?.forceNew) {
      const key = await this.createListenKey()
      return { listenKey: key, refreshed: true, source: 'created' }
    }
    if (this.listenKeyState) {
      const age = now - this.listenKeyState.updatedAt
      if (age < threshold) {
        return { listenKey: this.listenKeyState.key, refreshed: false, source: 'cached' }
      }
      try {
        await this.keepAliveListenKey()
        return { listenKey: this.listenKeyState.key, refreshed: true, source: 'refreshed' }
      } catch {
        // fallthrough to recreate
      }
    } else {
      const preset = process.env.ASTER_LISTEN_KEY || (this.cfg as any).listenKey
      if (preset) {
        this.listenKeyState = { key: preset, updatedAt: 0 }
        try {
          await this.keepAliveListenKey()
          return { listenKey: preset, refreshed: true, source: 'refreshed' }
        } catch {
          this.listenKeyState = undefined
        }
      }
    }
    const newKey = await this.createListenKey()
    return { listenKey: newKey, refreshed: true, source: 'created' }
  }

  getListenKeyState() {
    return this.listenKeyState ? { ...this.listenKeyState } : undefined
  }

  /**
   * 读取账户是否为双向持仓模式（true=双向，false=单向）。需要 cfg.apiKey/apiSecret。
   * 结果带有简单缓存，可通过 force=true 强制刷新。
   */
  async getDualSidePosition(force = false): Promise<boolean | undefined> {
    if (!force && typeof this.dualSideCache === 'boolean') return this.dualSideCache
    if (!this.cfg.apiKey || !this.cfg.apiSecret) return undefined
    try {
      const data = await this.hmacGet('/fapi/v3/positionSide/dual')
      const val = data && typeof data.dualSidePosition === 'boolean' ? data.dualSidePosition : undefined
      this.dualSideCache = val
      return val
    } catch {
      return undefined
    }
  }

  /**
   * 根据账户设置返回下单用 positionSide。
   * - 双向：BUY -> LONG，SELL -> SHORT
   * - 单向或未知：BOTH
   */
  async choosePositionSide(side: 'BUY' | 'SELL'): Promise<'BOTH' | 'LONG' | 'SHORT'> {
    const dual = await this.getDualSidePosition()
    if (dual === true) return side === 'BUY' ? 'LONG' : 'SHORT'
    return 'BOTH'
  }
}
