import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { get, post } from '@/utils/request'
import { unwrapSpotAssetPayload } from '@/utils/spotAsset'
import { resolveSpotOrderStatusKey, type SpotOrderStatusKey } from '@/utils/spotI18n'

export interface SpotSymbolInfo {
  symbol: string
  base: string
  quote: string
  /** POST config/symbol/list 每条自带的展示图标（S3 等外链） */
  icon: string | null
  pricePrecision: number
  volumePrecision: number
  depth0Pre: number
  depth1Pre: number
  depth2Pre: number
  minLimitPrice: number
  minLimitVolume: number
  minMarketBuy: number
  minMarketSell: number
  enableMarketBuy: boolean
  enableMarketSell: boolean
  makerFee: number
  takerFee: number
}

export interface SpotWalletAsset {
  coin: string
  balance: number
  frozenBalance: number
  total: number
}

export interface SpotOrder {
  id: string
  symbol: string
  side: 'buy' | 'sell'
  type: 'limit' | 'market'
  typeCode: number
  statusCode: number
  price: number
  amount: number
  tradedAmount: number
  tradedTurnover: number
  avgPrice: number
  /** 前端 i18n 键，见 spot.orderStatus.* */
  statusKey: SpotOrderStatusKey
  createTime: number | null
}

export interface SpotAccountRecord {
  id: string
  type: number
  amount: number
  symbol: string
  fee: number
  flag: number
  createTime: number | null
}

function num(v: unknown): number {
  if (v === undefined || v === null || v === '') {
    return 0
  }
  const n = Number(v)
  return Number.isNaN(n) ? 0 : n
}

function depthPreOr(raw: unknown, fallback: number): number {
  if (raw === undefined || raw === null || raw === '') return fallback
  const v = num(raw)
  if (!Number.isFinite(v)) return fallback
  return Math.min(8, Math.max(0, Math.round(v)))
}

/** 与 App SpotSymbolInfo._deriveBaseFallback 一致 */
function deriveBaseFromSymbol(sym: string): string {
  const s = sym.toUpperCase().replace(/[/\s-]/g, '')
  for (const q of ['USDT', 'USDC', 'BTC', 'ETH', 'BUSD']) {
    if (s.endsWith(q) && s.length > q.length) {
      return s.slice(0, -q.length)
    }
  }
  return s
}

/**
 * 解析 symbol/list 等配置接口的数组载荷。
 * 兼容：裸数组、{ data: [] }、{ records: [] }、以及 axios 已解包仍嵌套一层对象的情况。
 */
function extractConfigArray(raw: unknown): unknown[] {
  if (Array.isArray(raw)) return raw
  if (!raw || typeof raw !== 'object') return []
  const o = raw as Record<string, unknown>
  for (const key of ['data', 'records', 'list', 'items']) {
    const v = o[key]
    if (Array.isArray(v)) return v
  }
  const nested = o.data
  if (nested && typeof nested === 'object' && !Array.isArray(nested)) {
    const inner = nested as Record<string, unknown>
    for (const key of ['records', 'list', 'items']) {
      const v = inner[key]
      if (Array.isArray(v)) return v
    }
  }
  return []
}

/** symbol/list 单条上的图标字段（与后端 SpotSymbol 配置一致） */
function pickSymbolRowIcon(raw: Record<string, unknown>): string | null {
  for (const k of ['icon', 'logo', 'logoUrl', 'iconUrl', 'baseIcon', 'coinIcon']) {
    const v = raw[k]
    if (v == null) {
      continue
    }
    const s = String(v).trim()
    if (s && s !== 'null' && s !== 'undefined') {
      return s
    }
  }
  return null
}

function mapSymbol(raw: Record<string, unknown>): SpotSymbolInfo {
  const pricePre = num(raw.pricePre)
  const volPre = num(raw.volumePre)
  const mBuy = num(raw.marketBuyMin)
  const mSell = num(raw.marketSellMin)
  const symCompact = String(raw.symbol ?? '')
    .toUpperCase()
    .replace(/[/\s-]/g, '')
  let base = String(raw.base ?? '').toUpperCase().replace(/[/\s-]/g, '')
  let quote = String(raw.quote ?? 'USDT').toUpperCase().replace(/[/\s-]/g, '')
  if (!base && symCompact) base = deriveBaseFromSymbol(symCompact)
  if (!quote) quote = 'USDT'
  return {
    symbol: symCompact,
    base,
    quote,
    icon: pickSymbolRowIcon(raw),
    pricePrecision: pricePre ? Math.min(8, Math.max(0, Math.round(pricePre))) : 2,
    volumePrecision: volPre ? Math.min(8, Math.max(0, Math.round(volPre))) : 4,
    depth0Pre: depthPreOr(raw.depth0Pre, 2),
    depth1Pre: depthPreOr(raw.depth1Pre, 4),
    depth2Pre: depthPreOr(raw.depth2Pre, 6),
    minLimitPrice: num(raw.limitPriceMin),
    minLimitVolume: num(raw.limitVolumeMin),
    minMarketBuy: mBuy,
    minMarketSell: mSell,
    enableMarketBuy: mBuy > 0,
    enableMarketSell: mSell > 0,
    makerFee: num(raw.makerFee),
    takerFee: num(raw.takerFee),
  }
}

/** 路由参数 BTC-USDT → BTCUSDT */
export function apiSymbolFromRouteParam(param: string | undefined): string {
  return String(param || 'BTC-USDT').replace(/-/g, '').toUpperCase()
}

export function formatSpotSymbolForDisplay(sym: string): string {
  const s = sym.toUpperCase()
  if (s.endsWith('USDT')) {
    return `${s.slice(0, -4)}/USDT`
  }
  return s
}

function mapAssetRow(raw: Record<string, unknown>): SpotWalletAsset {
  const coin = String(raw.symbol ?? raw.coinId ?? raw.coin ?? '').toUpperCase()
  const balance = num(raw.available ?? raw.balance)
  const frozen = num(raw.frozen ?? raw.frozenBalance)
  return { coin, balance, frozenBalance: frozen, total: balance + frozen }
}

function mapOrder(raw: Record<string, unknown>): SpotOrder {
  const id = String(raw.id ?? raw.orderId ?? '')
  const sym = String(raw.symbol ?? '').toUpperCase()
  const sideRaw = String(raw.side ?? raw.direction ?? '').toUpperCase()
  const side: 'buy' | 'sell' = sideRaw === 'SELL' ? 'sell' : 'buy'
  const typeCode = num(raw.type)
  const type: 'limit' | 'market' = typeCode === 2 ? 'market' : 'limit'
  const statusCode = num(raw.status)
  const statusStr = raw.status != null && typeof raw.status !== 'number' ? String(raw.status) : ''
  return {
    id,
    symbol: sym,
    side,
    type,
    typeCode,
    statusCode,
    price: num(raw.price),
    amount: num(raw.volume ?? raw.amount),
    tradedAmount: num(raw.dealVolume ?? raw.tradedAmount),
    tradedTurnover: num(raw.dealMoney ?? raw.turnover),
    avgPrice: num(raw.avgPrice ?? raw.tradedAvgPrice),
    statusKey: resolveSpotOrderStatusKey(statusCode, statusStr),
    createTime: (() => {
      const c = raw.ctime ?? raw.time ?? raw.createTime
      if (c != null) {
        const n = Number(c)
        if (Number.isFinite(n) && n > 0) {
          return n < 10_000_000_000 ? n * 1000 : n
        }
        if (typeof c === 'string') {
          const parsed = Date.parse(c)
          return Number.isNaN(parsed) ? null : parsed
        }
      }
      return null
    })(),
  }
}

function extractOrderRecords(raw: unknown): unknown[] {
  if (Array.isArray(raw)) {
    return raw
  }
  if (!raw || typeof raw !== 'object') {
    return []
  }
  const o = raw as Record<string, unknown>
  if (Array.isArray(o.records)) {
    return o.records
  }
  const d = o.data
  if (d && typeof d === 'object') {
    const dr = (d as Record<string, unknown>).records
    if (Array.isArray(dr)) {
      return dr
    }
  }
  return []
}

function mapAccountRecord(raw: Record<string, unknown>): SpotAccountRecord {
  const ct = raw.createTime ?? raw.ctime ?? raw.time
  let createTime: number | null = null
  if (ct != null) {
    const n = Number(ct)
    if (Number.isFinite(n) && n > 0) {
      createTime = n < 10_000_000_000 ? n * 1000 : n
    } else {
      const parsed = Date.parse(String(ct))
      createTime = Number.isNaN(parsed) ? null : parsed
    }
  }
  return {
    id: String(raw.id ?? ''),
    type: num(raw.type),
    amount: num(raw.amount),
    symbol: String(raw.symbol ?? raw.coin ?? '').toUpperCase(),
    fee: num(raw.fee),
    flag: num(raw.flag),
    createTime,
  }
}

const PAGE_SIZE = 20

export const useSpotStore = defineStore('spot', () => {
  const symbolList = ref<SpotSymbolInfo[]>([])
  const symbolsLoading = ref(false)
  const symbolsError = ref<string | null>(null)

  const assetSummary = ref({
    totalAmount: '0',
    todayPnl: '0',
    todayPnlRate: '0',
  })
  const assets = ref<SpotWalletAsset[]>([])
  const assetsLoading = ref(false)

  const currentOrders = ref<SpotOrder[]>([])
  const ordersLoading = ref(false)

  const historyOrders = ref<SpotOrder[]>([])
  const historyOrderPage = ref(1)
  const historyOrderNoMore = ref(false)

  const historyDeals = ref<SpotOrder[]>([])
  const historyDealPage = ref(1)
  const historyDealNoMore = ref(false)

  const accountRecords = ref<SpotAccountRecord[]>([])
  const accountRecordPage = ref(1)
  const accountRecordNoMore = ref(false)

  const symbolMap = computed(() => {
    const m = new Map<string, SpotSymbolInfo>()
    for (const s of symbolList.value) {
      m.set(s.symbol, s)
    }
    return m
  })

  /** 币种图标映射：大写 coin symbol (如 "ETH") → icon URL */
  const coinIconMap = ref<Map<string, string>>(new Map())
  let coinIconsFetched = false

  async function fetchCoinIcons() {
    if (coinIconsFetched) return
    coinIconsFetched = true
    try {
      const raw = await get<unknown>('spot/v1/config/coin/list')
      const arr = Array.isArray(raw) ? raw
        : Array.isArray((raw as any)?.data) ? (raw as any).data
        : []
      const m = new Map<string, string>()
      for (const item of arr) {
        if (!item || typeof item !== 'object') {
          continue
        }
        const o = item as Record<string, unknown>
        const icon = String(o.icon ?? o.logo ?? o.logoUrl ?? o.iconUrl ?? '').trim()
        if (!icon) {
          continue
        }
        const keys = [
          o.symbol,
          o.coinSymbol,
          o.code,
          o.coinId,
          o.coin,
        ]
          .map(v => (v != null && String(v) !== '' ? String(v).toUpperCase().trim() : ''))
          .filter(Boolean)
        for (const coin of new Set(keys)) {
          if (coin) {
            m.set(coin, icon)
          }
        }
      }
      coinIconMap.value = m
    } catch { /* silent */ }
  }

  async function loadSymbols() {
    symbolsLoading.value = true
    symbolsError.value = null
    try {
      const raw = await get<unknown>('spot/v1/config/symbol/list')
      const arr = extractConfigArray(raw)
      symbolList.value = arr
        .filter((x): x is Record<string, unknown> => !!x && typeof x === 'object')
        .map(x => mapSymbol(x))
        .filter(s => s.symbol.length > 0)
    } catch (e) {
      symbolsError.value = e instanceof Error ? e.message : '加载失败'
      throw e
    } finally {
      symbolsLoading.value = false
    }
  }

  async function fetchAssets() {
    assetsLoading.value = true
    try {
      const raw = await get<unknown>('spot/v1/account/asset/list')
      const inner = unwrapSpotAssetPayload(raw)
      if (inner) {
        assetSummary.value = {
          totalAmount: String(inner.totalAmount ?? '0'),
          todayPnl: String(inner.todayPnl ?? '0'),
          todayPnlRate: String(inner.todayPnlRate ?? '0'),
        }
        const al = inner.assetList
        assets.value = Array.isArray(al)
          ? al.filter((x): x is Record<string, unknown> => !!x && typeof x === 'object').map(mapAssetRow)
          : []
      } else {
        assets.value = []
      }
    } catch {
      assets.value = []
    } finally {
      assetsLoading.value = false
    }
  }

  async function fetchCurrentOrders(symbol?: string) {
    ordersLoading.value = true
    try {
      const body: Record<string, unknown> = { page: 1, size: 50 }
      if (symbol) {
        body.symbol = symbol
      }
      const raw = await post<unknown>('spot/v1/order/current', body)
      const records = extractOrderRecords(raw)
      currentOrders.value = records
        .filter((x): x is Record<string, unknown> => !!x && typeof x === 'object')
        .map(mapOrder)
    } catch {
      currentOrders.value = []
    } finally {
      ordersLoading.value = false
    }
  }

  async function fetchHistoryOrders(refresh = false, symbol?: string) {
    if (!refresh && historyOrderNoMore.value) return
    if (refresh) { historyOrderPage.value = 1; historyOrderNoMore.value = false; historyOrders.value = [] }
    try {
      const body: Record<string, unknown> = { page: historyOrderPage.value, size: PAGE_SIZE }
      if (symbol) body.symbol = symbol
      const raw = await post<unknown>('spot/v1/order/history', body)
      const records = extractOrderRecords(raw)
      const list = records
        .filter((x): x is Record<string, unknown> => !!x && typeof x === 'object')
        .map(mapOrder)
      historyOrders.value = refresh ? list : [...historyOrders.value, ...list]
      if (list.length < PAGE_SIZE) historyOrderNoMore.value = true
      else historyOrderPage.value++
    } catch { /* silent */ }
  }

  async function fetchHistoryDeals(refresh = false, symbol?: string) {
    if (!refresh && historyDealNoMore.value) return
    if (refresh) { historyDealPage.value = 1; historyDealNoMore.value = false; historyDeals.value = [] }
    try {
      const body: Record<string, unknown> = { page: historyDealPage.value, size: PAGE_SIZE }
      if (symbol) body.symbol = symbol
      const raw = await post<unknown>('spot/v1/order/trades', body)
      const records = extractOrderRecords(raw)
      const list = records
        .filter((x): x is Record<string, unknown> => !!x && typeof x === 'object')
        .map(mapOrder)
      historyDeals.value = refresh ? list : [...historyDeals.value, ...list]
      if (list.length < PAGE_SIZE) historyDealNoMore.value = true
      else historyDealPage.value++
    } catch { /* silent */ }
  }

  async function fetchAccountRecords(refresh = false, symbol?: string) {
    if (!refresh && accountRecordNoMore.value) return
    if (refresh) { accountRecordPage.value = 1; accountRecordNoMore.value = false; accountRecords.value = [] }
    try {
      const body: Record<string, unknown> = { page: accountRecordPage.value, size: PAGE_SIZE }
      if (symbol) body.symbol = symbol
      const raw = await post<unknown>('spot/v1/account_records', body)
      const records = extractOrderRecords(raw)
      const list = records
        .filter((x): x is Record<string, unknown> => !!x && typeof x === 'object')
        .map(mapAccountRecord)
      accountRecords.value = refresh ? list : [...accountRecords.value, ...list]
      if (list.length < PAGE_SIZE) accountRecordNoMore.value = true
      else accountRecordPage.value++
    } catch { /* silent */ }
  }

  function balanceForCoin(coin: string): number {
    const c = coin.toUpperCase()
    return assets.value.find(a => a.coin === c)?.balance ?? 0
  }

  async function createOrder(payload: {
    symbol: string
    side: 'BUY' | 'SELL'
    type: 1 | 2
    price: number
    volume: number
  }) {
    await post('spot/v1/order/create', {
      symbol: payload.symbol,
      side: payload.side,
      type: payload.type,
      price: payload.price,
      volume: payload.volume,
      source: 2,
      orderType: 1,
    })
  }

  async function cancelOrder(orderId: number | string) {
    await post('spot/v1/order/cancel', { orderId: Number(orderId) })
  }

  async function cancelAllOrders(symbol?: string) {
    const pending = currentOrders.value.filter(o =>
      o.statusCode !== 2 && o.statusCode !== 4 && o.statusCode !== 5 && o.statusCode !== 6,
    )
    if (!pending.length) return
    let lastErr: Error | null = null
    for (const o of pending) {
      try { await cancelOrder(o.id) } catch (e) { lastErr = e instanceof Error ? e : new Error('撤单失败') }
    }
    await fetchCurrentOrders(symbol)
    if (lastErr) throw lastErr
  }

  /** WS type=asset，字段 accountList（与 App 一致） */
  function applyWsAssetPush(msg: Record<string, unknown>) {
    const list = msg.accountList
    if (!Array.isArray(list)) return
    if (list.length === 0) {
      void fetchAssets()
      return
    }
    const byCoin = new Map<string, SpotWalletAsset>()
    for (const a of assets.value) {
      byCoin.set(a.coin, a)
    }
    for (const raw of list) {
      if (!raw || typeof raw !== 'object') continue
      const row = mapAssetRow(raw as Record<string, unknown>)
      if (row.coin) byCoin.set(row.coin, row)
    }
    assets.value = Array.from(byCoin.values()).sort((x, y) => x.coin.localeCompare(y.coin))
  }

  /** WS type=order，字段 orderList / orderCount（与 App 一致） */
  function applyWsOrderPush(msg: Record<string, unknown>, filterSymbol?: string) {
    const list = msg.orderList
    if (!Array.isArray(list)) return
    const oc = msg.orderCount
    const totalPending = oc == null || oc === '' ? null : Number(oc)

    if (list.length === 0) {
      if (totalPending === 0) {
        currentOrders.value = []
      } else {
        void fetchCurrentOrders(filterSymbol)
      }
      return
    }

    const incoming = list
      .filter((x): x is Record<string, unknown> => !!x && typeof x === 'object')
      .map(mapOrder)

    const symbolsTouched = new Set(
      incoming.map(o => o.symbol.toUpperCase()).filter(s => s.length > 0),
    )
    if (symbolsTouched.size === 0) {
      currentOrders.value = incoming
      return
    }
    const kept = currentOrders.value.filter(o => !symbolsTouched.has(o.symbol.toUpperCase()))
    currentOrders.value = [...kept, ...incoming]
  }

  return {
    symbolList,
    symbolMap,
    symbolsLoading,
    symbolsError,
    assetSummary,
    assets,
    assetsLoading,
    currentOrders,
    ordersLoading,
    historyOrders,
    historyOrderNoMore,
    historyDeals,
    historyDealNoMore,
    accountRecords,
    accountRecordNoMore,
    coinIconMap,
    fetchCoinIcons,
    loadSymbols,
    fetchAssets,
    fetchCurrentOrders,
    fetchHistoryOrders,
    fetchHistoryDeals,
    fetchAccountRecords,
    balanceForCoin,
    createOrder,
    cancelOrder,
    cancelAllOrders,
    applyWsAssetPush,
    applyWsOrderPush,
  }
})