<script setup lang="ts">
import { ref, computed, watch, onMounted, onActivated } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { get, post, postForm } from '@/utils/request'
import { unwrapSpotAssetPayload, extractUsdtAvailableFromSpotAssets, involvesSpotTradingBridge } from '@/utils/spotAsset'
import {
  applyStakingOverviewBalances,
  loadStakingWalletSnapshot,
  parseStakingFromWalletGet,
  STAKING_COIN_UNIT,
} from '@/utils/stakingWallet'
import { useSpotTransferCoin } from '@/composables/useSpotTransferCoin'
import { useToast } from '@/composables/useToast'
import type { OtherWalletType, WalletType } from '@/stores/transfer'
import { normalizeTransferPair, OTHER_WALLET_TYPES } from '@/stores/transfer'
import { walletTypeLabel } from '@/utils/walletTypeLabel'
import { useCoinsStore } from '@/stores/coins'
import { useSpotStore } from '@/stores/spot'
import { storeToRefs } from 'pinia'
import { fetchStakingOrders, type StakingOrderItem } from '@/api/staking'

const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const toast = useToast()
const coinsStore = useCoinsStore()
const spotStore = useSpotStore()
const { coinIconMap } = storeToRefs(spotStore)

function wLabel(kind: WalletType | OtherWalletType) {
  return walletTypeLabel(t, kind)
}

// ── API 类型 ──────────────────────────────────────────────
interface TodayPnlAccountInfo {
  name: string
  balance: string
  frozenBalance: string
  frozenMargin: string
  currentCapital: string
}
interface TodayPnl {
  revenue: string
  revenueRate: string | null
  cashBalance: string
  currentCapital: string
  accountInfoList: TodayPnlAccountInfo[]
}

/**
 * 统一解析 /swap/wallet-new/get（兼容后端字段别名，并与 App 资产页口径一致）
 */
function normalizeSwapWalletGet(raw: unknown): TodayPnl {
  if (!raw || typeof raw !== 'object') {
    return {
      revenue: '0',
      revenueRate: null,
      cashBalance: '0',
      currentCapital: '0',
      accountInfoList: [],
    }
  }
  const r = raw as Record<string, unknown>
  const list = r.accountInfoList
  const revenueVal = r.revenue ?? r.todayRevenue ?? r.todayIncome ?? r.todayPnl ?? r.todayProfit ?? '0'
  const rateVal = r.revenueRate ?? r.todayPnlRate ?? r.todayRate
  return {
    revenue: String(revenueVal ?? '0'),
    revenueRate: rateVal == null || rateVal === '' ? null : String(rateVal),
    cashBalance: String(r.cashBalance ?? '0'),
    currentCapital: String(r.currentCapital ?? '0'),
    accountInfoList: Array.isArray(list) ? (list as TodayPnlAccountInfo[]) : [],
  }
}

interface SpotWalletRow {
  coin: string
  balance: number
  frozenBalance: number
  total: number
}

/** 资产页跟单 Tab 当前仓位（与 MyFollowingView mapPosition 字段对齐） */
interface AssetFollowPosition {
  id: string
  symbol: string
  direction: number
  openPrice: number
  closePrice: number
  amount: number
  lever: number
  profit: number
  profitRate: number
  traderName?: string
  isNoLoss: boolean
}

// ── State ─────────────────────────────────────────────────
const todayPnl = ref<TodayPnl | null>(null)
const spotTradingBal = ref('0')
const spotTotal = ref(0)
const spotTodayPnl = ref(0)
const spotTodayPnlRate = ref(0)
const spotWallets = ref<SpotWalletRow[]>([])
/** 现货资产列表：true=隐藏余额为 0 的币种（对接 hideZero，默认 true） */
const hideZeroAssets = ref(true)
const spotAssetsLoading = ref(false)
/** 可提现账户资产列表（/swap/wallet-new/account/asset/list?hideZero=） */
const fundWallets = ref<SpotWalletRow[]>([])
const fundTotalUsdt = ref(0)
const hideZeroFundAssets = ref(true)
const fundAssetsLoading = ref(false)
const loading = ref(false)
const error = ref<string | null>(null)
const activeTab = ref(0)
const ASSET_TABS = {
  OVERVIEW: 0,
  FUND: 1,
  SPOT: 2,
  SWAP: 3,
  FOLLOW: 4,
  STAKING: 5,
} as const
const obscure = ref(false)
/** 合约账户未实现盈亏：与 App 一致，来自 get-with-positions 持仓累加（不含体验金） */
const swapUnrealizedPnl = ref(0)
const followPositions = ref<AssetFollowPosition[]>([])
const followPosLoading = ref(false)
const followCloseBusyId = ref<string | null>(null)
/** 总览 iBit / 锁仓：wallet-new 质押账户 balance、frozenBalance */
const stakingOverviewAvailable = ref('0')
const stakingOverviewLocked = ref('0')
/** 已释放总量（后端 releasedTotal，勿用 frozenBalance） */
const stakingOverviewReleased = ref('0')
const stakingOrders = ref<StakingOrderItem[]>([])
const stakingOrdersLoading = ref(false)
const STAKING_ORDER_PREVIEW_SIZE = 5
const STAKING_PULL_REFRESH_THRESHOLD = 58
const stakingPullStartY = ref<number | null>(null)
const stakingPullDistance = ref(0)
type StakingOrderStatusFilter = 'all' | 'subscribing' | 'releasing' | 'completed'
const stakingOrderStatusFilter = ref<StakingOrderStatusFilter>('all')
const stakingOrdersRequestSeq = ref(0)
// SWAP=0, FOLLOW=1, SPOT=2（后端固定顺序，与 lang 无关）
const SWAP_IDX = 0
const FOLLOW_IDX = 1
const SPOT_IDX = 2

const totalUsdt = computed(() => {
  const list = todayPnl.value?.accountInfoList
  const base = list?.length ? list.reduce((s, a) => s + Number(a.currentCapital || 0), 0) : 0
  return base + spotTotal.value
})

/** 合约侧（wallet-new）今日盈亏，不含现货交易账户 */
const swapBookTodayPnl = computed(() => {
  if (!todayPnl.value) {
    return 0
  }
  const v = Number(todayPnl.value.revenue)
  return Number.isFinite(v) ? v : 0
})

/**
 * 总览顶部「今日盈亏」：与 App 一致 = wallet-new 汇总 + 现货接口 todayPnl
 * （原先仅用 revenue，漏计现货部分导致与总资产口径不一致）
 */
const headerTodayPnlAmount = computed(() => swapBookTodayPnl.value + spotTodayPnl.value)

/**
 * 今日盈亏比例：优先用（当前总资产 − 今日盈亏）作分母近似昨日净值；
 * 分母无效且仅合约侧有数据时回退接口 revenueRate。
 */
const headerTodayPnlRatePercent = computed((): number | null => {
  const total = totalUsdt.value
  const pnl = headerTodayPnlAmount.value
  const denom = total - pnl
  if (denom > 1e-8) {
    return (pnl / denom) * 100
  }
  if (spotTodayPnl.value === 0) {
    const rr = todayPnl.value?.revenueRate
    if (rr != null && rr !== '') {
      const x = Number(rr)
      if (Number.isFinite(x)) {
        return x
      }
    }
  }
  return null
})

const stakingRefreshText = computed(() => {
  if (stakingOrdersLoading.value) {
    return t('assets.refreshing')
  }
  if (stakingPullDistance.value >= STAKING_PULL_REFRESH_THRESHOLD) {
    return t('assets.releaseToRefresh')
  }
  return t('assets.pullToRefresh')
})

const stakingOrderStatusOptions = computed(() => [
  { value: 'all', label: t('common.all'), status: undefined },
  { value: 'subscribing', label: t('assets.subscribing'), status: '0' },
  { value: 'releasing', label: t('assets.releasing'), status: '1' },
  { value: 'completed', label: t('assets.completed'), status: '2' },
] as const)

function stakingOrderStatusParamOf(filter: StakingOrderStatusFilter): string | undefined {
  const option = stakingOrderStatusOptions.value.find(item => item.value === filter)
  return option?.status
}

const stakingOrderStatusParam = computed(() => stakingOrderStatusParamOf(stakingOrderStatusFilter.value))

function spotCoinIcon(coin: string): string {
  const key = coin.toUpperCase().trim()
  if (!key) {
    return ''
  }
  const fromConfig = coinIconMap.value.get(key)
  if (fromConfig) {
    return fromConfig
  }
  const c = coin.toLowerCase()
  return coinsStore.coins.find(x => x.symbol?.toLowerCase().startsWith(c + '/'))?.icon ?? ''
}

function getAccount(idx: number): TodayPnlAccountInfo | null {
  return todayPnl.value?.accountInfoList?.[idx] ?? null
}

function fmt(n: number | string, decimals = 2) {
  const v = Number(n)
  const x = Number.isFinite(v) ? v : 0
  return x.toLocaleString('en-US', { minimumFractionDigits: decimals, maximumFractionDigits: decimals })
}

function fmtSigned(n: number | string) {
  const v = Number(n)
  const x = Number.isFinite(v) ? v : 0
  const sign = x >= 0 ? '+' : ''
  return sign + fmt(x)
}

/** 单仓位未实现盈亏（对齐 Flutter FuturesPosition.fromJson：优先 currentProfit） */
function positionUnrealizedPnl(p: Record<string, unknown>): number {
  const cp = p.currentProfit
  if (cp != null && cp !== '') {
    const v = Number(cp)
    return Number.isFinite(v) ? v : 0
  }
  const dir = String(p.direction ?? 'BUY').toUpperCase()
  const live = Number(p.currentPrice ?? 0) || 0
  const open = Number(p.openPrice ?? 0) || 0
  const vol = Number(p.currentPosition ?? 0) || 0
  const diff = dir === 'BUY' || dir === '0' ? (live - open) : (open - live)
  return diff * vol
}

/** 汇总 SWAP 未实现盈亏（排除 experienceRecordId 体验金仓位） */
function sumSwapUnrealizedPnl(list: unknown[]): number {
  let s = 0
  for (const raw of list) {
    if (!raw || typeof raw !== 'object') {
      continue
    }
    const p = raw as Record<string, unknown>
    const exp = p.experienceRecordId
    if (exp != null && String(exp) !== '' && String(exp) !== 'null') {
      continue
    }
    s += positionUnrealizedPnl(p)
  }
  return s
}

async function refreshSwapUnrealizedFromPositions() {
  try {
    const posData = await get<{ currentPositionWithCutList?: unknown[] }>('/swap/wallet-new/get-with-positions')
    const rawList = posData?.currentPositionWithCutList
    swapUnrealizedPnl.value = Array.isArray(rawList) ? sumSwapUnrealizedPnl(rawList) : 0
  } catch {
    /* 保留上次值，避免切 tab 闪零 */
  }
}

const swapUnrealizedPnlClass = computed(() => {
  const v = swapUnrealizedPnl.value
  if (v > 0) {
    return 'up'
  }
  if (v < 0) {
    return 'down'
  }
  return ''
})

const followUnrealizedPnl = computed(() =>
  followPositions.value.reduce((s, p) => s + p.profit, 0),
)

const followUnrealizedPnlClass = computed(() => {
  const v = followUnrealizedPnl.value
  if (v > 0) {
    return 'up'
  }
  if (v < 0) {
    return 'down'
  }
  return ''
})

function normFollowSym(s: string): string {
  return s.toUpperCase().replace(/[/\-_ ]/g, '')
}

function fmtFollowSymbol(sym: string): string {
  if (sym.endsWith('USDT')) {
    return sym.slice(0, -4) + '/USDT'
  }
  if (sym.endsWith('BTC')) {
    return sym.slice(0, -3) + '/BTC'
  }
  if (sym.endsWith('ETH')) {
    return sym.slice(0, -3) + '/ETH'
  }
  return sym
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mapAssetFollowPosition(r: any): AssetFollowPosition {
  const pid =
    r.positionId != null && String(r.positionId) !== ''
      ? String(r.positionId)
      : (r.id != null && String(r.id) !== '' ? String(r.id) : '')
  return {
    id: pid || `row-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
    symbol: normFollowSym(r.symbol ?? r.coin ?? ''),
    direction: parseInt(r.direction ?? '0', 10),
    openPrice: parseFloat(r.openPrice ?? r.openRealPrice ?? '0') || 0,
    closePrice: parseFloat(r.closePrice ?? r.currentPrice ?? r.nowPrice ?? '0') || 0,
    amount: parseFloat(r.amount ?? r.volume ?? r.totalPosition ?? '0') || 0,
    lever: parseInt(r.lever ?? r.leverage ?? '1', 10) || 1,
    profit: parseFloat(r.profit ?? r.profitAmount ?? '0') || 0,
    profitRate: parseFloat(r.profitRate ?? r.yieldRate ?? '0') || 0,
    traderName: r.traderName ?? r.nickname ?? r.traderNickname ?? '',
    isNoLoss: !!(r.specialTrader ?? r.isNoLoss),
  }
}

function canCloseAssetFollowPosition(p: AssetFollowPosition): boolean {
  if (p.isNoLoss) {
    return false
  }
  if (!p.id || p.id.startsWith('row-')) {
    return false
  }
  return true
}

async function refreshFollowCopyPositions() {
  followPosLoading.value = true
  try {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const res: any = await post('/follow/position/current-follow', { currentPage: 1, pageSize: 50 })
    const list = Array.isArray(res) ? res : (res?.records ?? res?.list ?? res?.data ?? [])
    followPositions.value = Array.isArray(list) ? list.map(mapAssetFollowPosition) : []
  } catch {
    followPositions.value = []
  } finally {
    followPosLoading.value = false
  }
}

async function closeAssetFollowPosition(p: AssetFollowPosition) {
  if (p.isNoLoss) {
    toast.error(t('assets.noLossPosition'))
    return
  }
  if (!canCloseAssetFollowPosition(p)) {
    toast.error(t('assets.invalidPosition'))
    return
  }
  if (!confirm(t('assets.confirmClose'))) {
    return
  }
  const positionId = String(p.id)
  followCloseBusyId.value = positionId
  try {
    await postForm('/swap/order/close-all', { positionId })
    toast.success(t('assets.closeSuccess'))
    await Promise.all([refreshFollowCopyPositions(), loadAssets({ silent: true })])
  } catch (e: unknown) {
    toast.error(e instanceof Error ? e.message : t('assets.closeFailed'))
  } finally {
    followCloseBusyId.value = null
  }
}

/** 现货交易账户资产列表（spot/v1/account/asset/list?hideZero=） */
async function loadSpotAssetList(opts?: { silent?: boolean }) {
  const silent = opts?.silent ?? false
  if (!silent) {
    spotAssetsLoading.value = true
  }
  try {
    const raw = await get<unknown>('spot/v1/account/asset/list', {
      hideZero: hideZeroAssets.value,
    })
    spotTradingBal.value = extractUsdtAvailableFromSpotAssets(raw)
    const inner = unwrapSpotAssetPayload(raw)
    if (inner) {
      spotTotal.value = Number(inner.totalAmount ?? 0)
      spotTodayPnl.value = Number(inner.todayPnl ?? 0)
      spotTodayPnlRate.value = Number(inner.todayPnlRate ?? 0)
      const al = inner.assetList
      spotWallets.value = Array.isArray(al)
        ? (al as Record<string, unknown>[])
            .filter(x => !!x && typeof x === 'object')
            .map(x => {
              const coin = String(x.symbol ?? x.coinId ?? x.coin ?? '').toUpperCase()
              const balance = Number(x.available ?? x.balance ?? 0)
              const frozen = Number(x.frozen ?? x.frozenBalance ?? 0)
              return { coin, balance, frozenBalance: frozen, total: balance + frozen }
            })
            .filter(w => w.coin.length > 0)
        : []
    } else {
      spotWallets.value = []
    }
  } catch {
    spotTradingBal.value = '0'
    spotWallets.value = []
  } finally {
    if (!silent) {
      spotAssetsLoading.value = false
    }
  }
}

function toggleHideZeroAssets() {
  hideZeroAssets.value = !hideZeroAssets.value
  void loadSpotAssetList({ silent: true })
}

/** 可提现账户资产列表（wallet-new/account/asset/list?hideZero=） */
async function loadFundAssetList(opts?: { silent?: boolean }) {
  const silent = opts?.silent ?? false
  if (!silent) {
    fundAssetsLoading.value = true
  }
  try {
    const raw = await get<unknown>('/swap/wallet-new/account/asset/list', {
      hideZero: hideZeroFundAssets.value,
    })
    const inner = unwrapSpotAssetPayload(raw)
    if (inner) {
      fundTotalUsdt.value = Number(inner.totalAmount ?? 0)
      const al = inner.assetList
      fundWallets.value = Array.isArray(al)
        ? (al as Record<string, unknown>[])
            .filter(x => !!x && typeof x === 'object')
            .map(x => {
              const coin = String(x.symbol ?? x.coinId ?? x.coin ?? '').toUpperCase()
              const balance = Number(x.available ?? x.balance ?? 0)
              const frozen = Number(x.frozen ?? x.frozenBalance ?? 0)
              const total = Number(x.total ?? balance + frozen)
              return { coin, balance, frozenBalance: frozen, total }
            })
            .filter(w => w.coin.length > 0)
        : []
    } else {
      fundWallets.value = []
      fundTotalUsdt.value = 0
    }
  } catch {
    fundWallets.value = []
    fundTotalUsdt.value = 0
  } finally {
    if (!silent) {
      fundAssetsLoading.value = false
    }
  }
}

function toggleHideZeroFundAssets() {
  hideZeroFundAssets.value = !hideZeroFundAssets.value
  void loadFundAssetList({ silent: true })
}

/**
 * 拉取资产数据（对齐 App AssetScreen：切换子 Tab 时 silentRefresh + 跟单 Tab 额外拉仓位）
 * @param opts.silent 为 true 时不展示全页 loading、失败不 toast（用于 Tab 切换刷新）
 */
async function loadAssets(opts?: { silent?: boolean }) {
  const silent = opts?.silent ?? false
  if (!silent) {
    loading.value = true
    error.value = null
  }
  try {
    const [pnlRaw] = await Promise.all([
      get<unknown>('/swap/wallet-new/get'),
      refreshSwapUnrealizedFromPositions(),
    ])
    todayPnl.value = normalizeSwapWalletGet(pnlRaw)
    const walletStaking = parseStakingFromWalletGet(pnlRaw)
    let apiStaking = null
    try {
      apiStaking = await loadStakingWalletSnapshot(STAKING_COIN_UNIT)
    } catch {
      apiStaking = null
    }
    const merged = applyStakingOverviewBalances(walletStaking, apiStaking)
    stakingOverviewAvailable.value = merged.overview.available
    stakingOverviewLocked.value = merged.overview.locked
    stakingOverviewReleased.value = merged.overview.released
    await Promise.all([
      loadSpotAssetList({ silent: true }),
      loadFundAssetList({ silent: true }),
    ])
  } catch (e) {
    if (!silent) {
      error.value = e instanceof Error ? e.message : t('common.loadFailed')
      toast.error(error.value)
    }
  } finally {
    if (!silent) {
      loading.value = false
    }
  }
}

async function loadStakingOrders(status = stakingOrderStatusParam.value, opts?: { clear?: boolean }) {
  const requestSeq = stakingOrdersRequestSeq.value + 1
  stakingOrdersRequestSeq.value = requestSeq
  stakingOrdersLoading.value = true
  if (opts?.clear) {
    stakingOrders.value = []
  }
  try {
    const page = await fetchStakingOrders(1, STAKING_ORDER_PREVIEW_SIZE, status)
    if (requestSeq !== stakingOrdersRequestSeq.value) {
      return
    }
    stakingOrders.value = Array.isArray(page?.content) ? page.content : []
  } catch {
    if (requestSeq !== stakingOrdersRequestSeq.value) {
      return
    }
    stakingOrders.value = []
  } finally {
    if (requestSeq === stakingOrdersRequestSeq.value) {
      stakingOrdersLoading.value = false
    }
  }
}

function changeStakingOrderStatus(event: Event) {
  const next = (event.target as HTMLSelectElement | null)?.value as StakingOrderStatusFilter | undefined
  if (!next) {
    return
  }
  stakingOrderStatusFilter.value = next
  void loadStakingOrders(stakingOrderStatusParamOf(next), { clear: true })
}

function beginStakingPull(event: TouchEvent) {
  if (stakingOrdersLoading.value) {
    return
  }
  stakingPullStartY.value = event.touches[0]?.clientY ?? null
}

function moveStakingPull(event: TouchEvent) {
  if (stakingPullStartY.value == null || stakingOrdersLoading.value) {
    return
  }
  const currentY = event.touches[0]?.clientY
  if (currentY == null) {
    return
  }
  const delta = currentY - stakingPullStartY.value
  stakingPullDistance.value = delta > 0 ? Math.min(delta / 2, 82) : 0
}

async function refreshStakingOrdersByPull() {
  stakingPullDistance.value = STAKING_PULL_REFRESH_THRESHOLD
  await loadStakingOrders()
  stakingPullDistance.value = 0
}

function endStakingPull() {
  const shouldRefresh = stakingPullDistance.value >= STAKING_PULL_REFRESH_THRESHOLD
  stakingPullStartY.value = null
  if (shouldRefresh) {
    void refreshStakingOrdersByPull()
    return
  }
  stakingPullDistance.value = 0
}

function applyTabFromRoute() {
  const raw = route.query.tab
  const v = Array.isArray(raw) ? raw[0] : raw
  if (v === 'staking' || v === 'locked') {
    activeTab.value = ASSET_TABS.STAKING
  }
}

watch(() => route.query.tab, () => {
  applyTabFromRoute()
})

watch(activeTab, (tabIdx) => {
  void loadAssets({ silent: true })
  if (tabIdx === ASSET_TABS.FUND) {
    void loadFundAssetList({ silent: true })
  } else if (tabIdx === ASSET_TABS.SPOT) {
    void loadSpotAssetList({ silent: true })
  } else if (tabIdx === ASSET_TABS.FOLLOW) {
    void refreshFollowCopyPositions()
  } else if (tabIdx === ASSET_TABS.STAKING) {
    void loadStakingOrders()
  }
})

onMounted(() => {
  applyTabFromRoute()
  if (!coinsStore.fetched) {
    coinsStore.fetchCoins()
  }
  void spotStore.fetchCoinIcons()
  loadAssets()
})
onActivated(() => {
  applyTabFromRoute()
  loadAssets()
  if (activeTab.value === ASSET_TABS.FOLLOW) {
    void refreshFollowCopyPositions()
  } else if (activeTab.value === ASSET_TABS.STAKING) {
    void loadStakingOrders()
  }
})

// ── 快捷操作 ──────────────────────────────────────────────
const tabs = computed(() => [
  t('assets.overview'),
  t('assets.fund'),
  t('assets.spot'),
  t('assets.futures'),
  t('assets.copyTrade'),
  t('assets.stakingTab'),
])

// ── 划转弹窗（资产页内快速划转，与独立路由 TransferView 并存）────
const showTransfer = ref(false)
const transferFrom = ref<WalletType>('SPOT')
const transferTo = ref<WalletType>('SWAP')
const transferAmount = ref('')
const transferLoading = ref(false)
const transferError = ref('')

const {
  symbol: transferSymbol,
  coinOptions,
  assetsLoading: transferCoinLoading,
  loadAssets: loadTransferCoinAssets,
  availableFor: transferAvailableFor,
} = useSpotTransferCoin('USDT')

const isSpotCoinTransfer = computed(() =>
  involvesSpotTradingBridge(transferFrom.value, transferTo.value),
)

const transferFromAvailable = computed(() => {
  if (isSpotCoinTransfer.value) {
    return transferAvailableFor(transferFrom.value, transferSymbol.value)
  }
  return getBalance(transferFrom.value)
})

async function openTransfer(from: WalletType = 'SPOT', to: WalletType = 'SWAP') {
  const [f, toWallet] = normalizeTransferPair(from, to)
  transferFrom.value = f
  transferTo.value = toWallet
  transferAmount.value = ''
  transferError.value = ''
  showTransfer.value = true
  if (involvesSpotTradingBridge(f, toWallet)) {
    void loadTransferCoinAssets()
  }
}

watch(transferSymbol, () => {
  transferAmount.value = ''
})

watch([transferFrom, transferTo], async ([f, toW]) => {
  if (!showTransfer.value) return
  transferAmount.value = ''
  if (involvesSpotTradingBridge(f, toW)) {
    void loadTransferCoinAssets()
  }
})

async function swapTransfer() {
  const [a, b] = normalizeTransferPair(transferTo.value, transferFrom.value)
  transferFrom.value = a
  transferTo.value = b
  transferAmount.value = ''
}

function getBalance(type: WalletType): string {
  if (type === 'SPOT_TRADING') {
    return spotTradingBal.value
  }
  const idxMap: Record<'SWAP' | 'FOLLOW' | 'SPOT', number> = { SWAP: SWAP_IDX, FOLLOW: FOLLOW_IDX, SPOT: SPOT_IDX }
  const idx = idxMap[type as 'SWAP' | 'FOLLOW' | 'SPOT']
  return todayPnl.value?.accountInfoList?.[idx]?.balance ?? '0'
}

async function doTransfer() {
  transferError.value = ''
  const [f, toWallet] = normalizeTransferPair(transferFrom.value, transferTo.value)
  transferFrom.value = f
  transferTo.value = toWallet
  if (transferFrom.value === transferTo.value) { transferError.value = t('assets.sameAccountError'); return }
  const amt = Number(transferAmount.value)
  if (!amt || amt <= 0) { transferError.value = t('assets.amountError'); return }
  if (amt > Number(transferFromAvailable.value)) { transferError.value = t('assets.insufficientBalance'); return }
  transferLoading.value = true
  try {
    if (f === 'SPOT_TRADING' || toWallet === 'SPOT_TRADING') {
      const direction = f === 'SPOT' ? 1 : 2
      await post('/spot/v1/account/transfer', {
        symbol: transferSymbol.value,
        amount: amt,
        direction,
      })
    } else {
      await postForm('/uc/asset/transfer', { amount: transferAmount.value, from: f, to: toWallet })
    }
    toast.success(t('assets.transferSuccess'))
    showTransfer.value = false
    await loadAssets()
  } catch (e) {
    transferError.value = e instanceof Error ? e.message : t('assets.transferFailed')
  } finally {
    transferLoading.value = false
  }
}

/** 总览列表划转：与原来三行逻辑一致，并包含现货账户 */
function openOverviewTransfer(type: WalletType) {
  if (type === 'SWAP') {
    openTransfer('SWAP', 'SPOT')
  } else if (type === 'FOLLOW') {
    openTransfer('FOLLOW', 'SPOT')
  } else if (type === 'SPOT_TRADING') {
    openTransfer('SPOT_TRADING', 'SPOT')
  } else {
    openTransfer('SPOT', 'SWAP')
  }
}

type OverviewRowKind = WalletType | 'STAKING_LOCKED'

function overviewAccountTab(kind: OverviewRowKind): number {
  if (kind === 'SPOT') {
    return ASSET_TABS.FUND
  }
  if (kind === 'SPOT_TRADING') {
    return ASSET_TABS.SPOT
  }
  if (kind === 'SWAP') {
    return ASSET_TABS.SWAP
  }
  if (kind === 'FOLLOW') {
    return ASSET_TABS.FOLLOW
  }
  return ASSET_TABS.STAKING
}

function selectOverviewAccount(kind: OverviewRowKind) {
  activeTab.value = overviewAccountTab(kind)
}

const OVERVIEW_ACCOUNT_ROWS: { kind: OverviewRowKind; idx?: number; canTransfer?: boolean }[] = [
  { kind: 'SPOT', idx: SPOT_IDX, canTransfer: true },
  { kind: 'SPOT_TRADING', canTransfer: true },
  { kind: 'SWAP', idx: SWAP_IDX, canTransfer: true },
  { kind: 'FOLLOW', idx: FOLLOW_IDX, canTransfer: true },
  { kind: 'STAKING_LOCKED', canTransfer: false },
]

function overviewAccountLabel(kind: OverviewRowKind): string {
  if (kind === 'STAKING_LOCKED') {
    return t('assets.lockedStakingAccount')
  }
  return wLabel(kind as WalletType)
}

function overviewAccountBalance(kind: OverviewRowKind, idx?: number): number {
  if (kind === 'STAKING_LOCKED') {
    return Number(stakingOverviewLocked.value)
  }
  if (kind === 'SPOT_TRADING') {
    return spotTotal.value
  }
  if (idx !== undefined) {
    return Number(getAccount(idx)?.currentCapital ?? 0)
  }
  return 0
}

function overviewAccountUnit(kind: OverviewRowKind): string {
  if (kind === 'STAKING_LOCKED') {
    return t('assets.ibitUnit')
  }
  return 'USDT'
}

function overviewAccountIcon(kind: OverviewRowKind): string {
  if (kind === 'STAKING_LOCKED') {
    return '🔒'
  }
  if (kind === 'SWAP') {
    return '📈'
  }
  if (kind === 'FOLLOW') {
    return '📋'
  }
  if (kind === 'SPOT_TRADING') {
    return '📊'
  }
  return '💰'
}

function stakingPendingAmount(order: StakingOrderItem): number {
  const amount = Number(order.amount ?? 0)
  const released = Number(order.releasedAmount ?? 0)
  const pending = amount - released
  return Number.isFinite(pending) && pending > 0 ? pending : 0
}

function stakingOrderCoinUnit(order: StakingOrderItem): string {
  const u = order.coinUnit?.trim()
  if (!u) return 'IBIT'
  return u.toUpperCase() === 'IBIT' ? 'IBIT' : u
}

function formatStakingOrderId(order: StakingOrderItem): string {
  return `ORD${order.id}`
}

function formatStakingOrderAmount(
  order: StakingOrderItem,
  value: string | number | undefined,
): string {
  if (obscure.value) return '****'
  return `${fmt(value ?? 0)} ${stakingOrderCoinUnit(order)}`
}

function shortDate(value?: string) {
  if (!value) {
    return '-'
  }
  return value.replace('T', ' ').slice(0, 10)
}
</script>

<template>
  <main class="page-main">
    <div class="page-container content">
      <h1 class="page-title">{{ t('assets.title') }}</h1>

      <div v-if="loading && !todayPnl" class="loading-state">
        <div class="spinner" />
      </div>

      <template v-else>
        <!-- 总资产估值 -->
        <div class="header-card dark-card">
          <div class="header-left">
            <p class="label-sm">{{ t('assets.totalAccount') }}</p>
            <div class="total-row">
              <span v-if="!obscure" class="total-num">{{ fmt(totalUsdt) }}</span>
              <span v-else class="total-num">****</span>
              <span class="unit">USDT</span>
              <button
                type="button"
                class="eye-btn"
                :aria-label="obscure ? t('assets.showBalance') : t('assets.hideBalance')"
                @click="obscure = !obscure"
              >
                <!-- 隐藏态：与币安 dashboard 隐藏图标一致（eye-off） -->
                <svg
                  v-if="obscure"
                  class="eye-icon"
                  viewBox="0 0 24 24"
                  xmlns="http://www.w3.org/2000/svg"
                  aria-hidden="true"
                >
                  <path
                    d="M19.864 2.863a.9.9 0 011.273 1.273L18.35 6.924c1.493 1.247 2.723 2.65 3.491 3.603.806 1 .788 2.413-.054 3.39-.85.985-2.243 2.458-3.921 3.692-1.66 1.22-3.713 2.291-5.865 2.291-1.78 0-3.488-.732-4.96-1.669l-3.403 3.405a.9.9 0 11-1.273-1.273l17.5-17.5zm-5.47 8.016l1.177 1.157-3.535 3.535-1.158-1.178-2.53 2.53C9.557 17.642 10.805 18.1 12 18.1c1.583 0 3.255-.805 4.8-1.94 1.526-1.122 2.817-2.484 3.622-3.418a.834.834 0 00.016-1.085c-.752-.934-1.947-2.287-3.368-3.453l-2.677 2.676zM12 4.099a6.82 6.82 0 011.728.233c.19.05.377.106.562.169a.9.9 0 01-.58 1.704 6.415 6.415 0 00-.439-.132A5.018 5.018 0 0012 5.9c-1.577 0-3.253.863-4.81 2.09-1.535 1.21-2.83 2.675-3.629 3.666a.837.837 0 00.017 1.087c.358.414.815.917 1.348 1.447a.9.9 0 01-1.27 1.276 27.858 27.858 0 01-1.441-1.547 2.637 2.637 0 01-.056-3.392c.84-1.043 2.232-2.623 3.917-3.951C7.739 5.266 9.814 4.099 12 4.099z"
                    fill="currentColor"
                  />
                </svg>
                <!-- 显示态：同系填充风格小眼睛（Heroicons 24 solid，与常见交易所一致） -->
                <svg
                  v-else
                  class="eye-icon"
                  viewBox="0 0 24 24"
                  xmlns="http://www.w3.org/2000/svg"
                  aria-hidden="true"
                >
                  <path d="M12 15a3 3 0 100-6 3 3 0 000 6z" fill="currentColor" />
                  <path
                    fill-rule="evenodd"
                    d="M1.323 11.447C2.811 6.976 7.028 4 12 4c4.971 0 9.185 2.974 10.677 7.447a1 1 0 010 .105C21.186 16.025 16.971 19 12 19c-4.97 0-9.185-2.974-10.676-7.447a1 1 0 010-.105zM12 16a4 4 0 100-8 4 4 0 000 8z"
                    clip-rule="evenodd"
                    fill="currentColor"
                  />
                </svg>
              </button>
            </div>
            <p v-if="todayPnl" class="pnl-row">
              <span class="pnl-label">{{ t('assets.todayPnl') }}</span>
              <span :class="headerTodayPnlAmount >= 0 ? 'up' : 'down'">
                {{ fmtSigned(headerTodayPnlAmount) }} USDT
              </span>
              <span
                v-if="headerTodayPnlRatePercent != null"
                :class="headerTodayPnlRatePercent >= 0 ? 'up' : 'down'"
              >
                ({{ fmtSigned(headerTodayPnlRatePercent) }}%)
              </span>
            </p>
          </div>
          <div class="header-actions">
            <button class="act-btn act-primary" @click="router.push('/assets/deposit')">{{ t('assets.depositCoin') }}</button>
            <button class="act-btn" @click="router.push('/assets/withdraw')">{{ t('assets.withdrawCoin') }}</button>
            <button class="act-btn" @click="openTransfer()">{{ t('assets.transfer') }}</button>
          </div>
        </div>

        <!-- 子 Tab 导航 -->
        <div class="tab-bar">
          <button
            v-for="(tab, i) in tabs"
            :key="i"
            class="tab-item"
            :class="{ active: activeTab === i }"
            @click="activeTab = i"
          >{{ tab }}</button>
        </div>

        <!-- Tab 0：总览 -->
        <template v-if="activeTab === ASSET_TABS.OVERVIEW">
          <div class="account-list">
            <div
              v-for="row in OVERVIEW_ACCOUNT_ROWS"
              :key="row.kind"
              class="account-row dark-card"
              role="button"
              tabindex="0"
              @click="selectOverviewAccount(row.kind)"
              @keydown.enter="selectOverviewAccount(row.kind)"
              @keydown.space.prevent="selectOverviewAccount(row.kind)"
            >
              <div class="acc-left">
                <span class="acc-icon">{{ overviewAccountIcon(row.kind) }}</span>
                <span class="acc-label">{{ overviewAccountLabel(row.kind) }}</span>
              </div>
              <div class="acc-right">
                <span v-if="!obscure" class="acc-balance">{{ fmt(overviewAccountBalance(row.kind, row.idx)) }}</span>
                <span v-else class="acc-balance">****</span>
                <span class="acc-unit">{{ overviewAccountUnit(row.kind) }}</span>
                <button
                  v-if="row.canTransfer !== false"
                  class="mini-btn"
                  @click.stop="openOverviewTransfer(row.kind as WalletType)"
                >
                  {{ t('assets.transfer') }}
                </button>
              </div>
            </div>
          </div>
        </template>

        <!-- Tab 1：可提现账户 -->
        <template v-if="activeTab === ASSET_TABS.FUND">
          <div class="spot-summary dark-card">
            <div class="spot-sum-left">
              <p class="label-sm">{{ t('assets.fundAccount') }}</p>
              <div class="total-row">
                <span class="total-num">{{ obscure ? '****' : fmt(fundTotalUsdt) }}</span>
                <span class="unit">USDT</span>
              </div>
            </div>
            <div class="spot-sum-actions fund-sum-actions">
              <button class="act-btn act-primary" type="button" @click="router.push('/assets/deposit')">{{ t('assets.depositCoin') }}</button>
              <button class="act-btn act-primary" type="button" @click="router.push('/assets/withdraw')">{{ t('assets.withdrawCoin') }}</button>
              <button class="act-btn act-outline" type="button" @click="openTransfer('SPOT', 'SPOT_TRADING')">{{ t('assets.transfer') }}</button>
            </div>
          </div>
          <div class="spot-list-toolbar">
            <button
              type="button"
              class="hide-zero-toggle"
              :class="{ active: hideZeroFundAssets }"
              :aria-pressed="hideZeroFundAssets"
              @click="toggleHideZeroFundAssets"
            >
              <span class="hide-zero-check" aria-hidden="true" />
              <span>{{ t('assets.hideZeroBalance') }}</span>
            </button>
          </div>
          <div v-if="fundAssetsLoading" class="empty-spot">{{ t('common.loading') }}</div>
          <div v-else-if="!fundWallets.length" class="empty-spot">{{ t('common.noData') }}</div>
          <div v-else class="spot-wallet-list">
            <div v-for="w in fundWallets" :key="w.coin" class="spot-wallet-row dark-card">
              <div class="sw-coin">
                <div class="sw-icon">
                  <img v-if="spotCoinIcon(w.coin)" :src="spotCoinIcon(w.coin)" :alt="w.coin" />
                  <span v-else class="sw-icon-fb">{{ w.coin[0] }}</span>
                </div>
                <span class="sw-name">{{ w.coin }}</span>
              </div>
              <div class="sw-amounts">
                <div class="sw-cell">
                  <span class="sw-lbl">{{ t('assets.assetBalance') }}</span>
                  <span class="sw-val">{{ obscure ? '****' : w.total.toFixed(4) }}</span>
                </div>
                <div class="sw-cell">
                  <span class="sw-lbl">{{ t('assets.available') }}</span>
                  <span class="sw-val">{{ obscure ? '****' : w.balance.toFixed(4) }}</span>
                </div>
                <div class="sw-cell">
                  <span class="sw-lbl">{{ t('assets.unavailable') }}</span>
                  <span class="sw-val">{{ obscure ? '****' : w.frozenBalance.toFixed(4) }}</span>
                </div>
              </div>
            </div>
          </div>
        </template>

        <!-- Tab 2：现货账户 -->
        <template v-if="activeTab === ASSET_TABS.SPOT">
          <div class="spot-summary dark-card">
            <div class="spot-sum-left">
              <p class="label-sm">{{ t('assets.totalAssets') }}</p>
              <div class="total-row">
                <span class="total-num">{{ obscure ? '****' : fmt(spotTotal) }}</span>
                <span class="unit">USDT</span>
              </div>
            </div>
            <div class="spot-sum-actions">
              <button class="act-btn act-primary" @click="openTransfer('SPOT', 'SPOT_TRADING')">{{ t('assets.transfer') }}</button>
              <button class="act-btn act-outline" @click="router.push('/spot/BTC-USDT')">{{ t('assets.goSpotTrade') }}</button>
            </div>
          </div>
          <div class="spot-list-toolbar">
            <button
              type="button"
              class="hide-zero-toggle"
              :class="{ active: hideZeroAssets }"
              :aria-pressed="hideZeroAssets"
              @click="toggleHideZeroAssets"
            >
              <span class="hide-zero-check" aria-hidden="true" />
              <span>{{ t('assets.hideZeroBalance') }}</span>
            </button>
          </div>
          <div v-if="spotAssetsLoading" class="empty-spot">{{ t('common.loading') }}</div>
          <div v-else-if="!spotWallets.length" class="empty-spot">{{ t('common.noData') }}</div>
          <div v-else class="spot-wallet-list">
            <div v-for="w in spotWallets" :key="w.coin" class="spot-wallet-row dark-card">
              <div class="sw-coin">
                <div class="sw-icon">
                  <img v-if="spotCoinIcon(w.coin)" :src="spotCoinIcon(w.coin)" :alt="w.coin" />
                  <span v-else class="sw-icon-fb">{{ w.coin[0] }}</span>
                </div>
                <span class="sw-name">{{ w.coin }}</span>
              </div>
              <div class="sw-amounts">
                <div class="sw-cell">
                  <span class="sw-lbl">{{ t('assets.assetBalance') }}</span>
                  <span class="sw-val">{{ obscure ? '****' : w.total.toFixed(4) }}</span>
                </div>
                <div class="sw-cell">
                  <span class="sw-lbl">{{ t('assets.available') }}</span>
                  <span class="sw-val">{{ obscure ? '****' : w.balance.toFixed(4) }}</span>
                </div>
                <div class="sw-cell">
                  <span class="sw-lbl">{{ t('assets.unavailable') }}</span>
                  <span class="sw-val">{{ obscure ? '****' : w.frozenBalance.toFixed(4) }}</span>
                </div>
              </div>
            </div>
          </div>
        </template>

        <!-- Tab 3：合约账户 -->
        <template v-if="activeTab === ASSET_TABS.SWAP">
          <div class="detail-card dark-card">
            <div class="detail-header">{{ t('assets.swapAccount') }}</div>
            <div class="swap-equity-block">
              <div class="total-row">
                <span v-if="!obscure" class="total-num">{{ fmt(getAccount(SWAP_IDX)?.currentCapital ?? 0) }}</span>
                <span v-else class="total-num">****</span>
                <span class="unit">USDT</span>
              </div>
            </div>
            <div class="detail-row">
              <span class="detail-label">{{ t('assets.walletBalance') }} (USDT)</span>
              <span class="detail-val">{{ obscure ? '****' : fmt(getAccount(SWAP_IDX)?.balance ?? 0) }}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">{{ t('assets.unrealizedPnl') }} (USDT)</span>
              <span class="detail-val" :class="swapUnrealizedPnlClass">{{ obscure ? '****' : fmtSigned(swapUnrealizedPnl) }}</span>
            </div>
            <div class="quick-actions">
              <button class="act-btn act-primary" @click="router.push('/assets/deposit')">{{ t('assets.depositCoin') }}</button>
              <button class="act-btn" @click="router.push('/assets/withdraw')">{{ t('assets.withdrawCoin') }}</button>
              <button class="act-btn" @click="openTransfer('SWAP', 'SPOT')">{{ t('assets.transfer') }}</button>
            </div>
          </div>
        </template>

        <!-- Tab 4：跟单账户 -->
        <template v-if="activeTab === ASSET_TABS.FOLLOW">
          <div class="detail-card dark-card">
            <div class="detail-header">{{ t('assets.followAccount') }}</div>
            <div class="swap-equity-block">
              <div class="total-row">
                <span v-if="!obscure" class="total-num">{{ fmt(getAccount(FOLLOW_IDX)?.currentCapital ?? 0) }}</span>
                <span v-else class="total-num">****</span>
                <span class="unit">USDT</span>
              </div>
            </div>
            <div class="detail-row">
              <span class="detail-label">{{ t('assets.followBalance') }} (USDT)</span>
              <span class="detail-val">{{ obscure ? '****' : fmt(getAccount(FOLLOW_IDX)?.balance ?? 0) }}</span>
            </div>
            <div class="detail-row">
              <span class="detail-label">{{ t('assets.unrealizedPnl') }} (USDT)</span>
              <span class="detail-val" :class="followUnrealizedPnlClass">{{ obscure ? '****' : fmtSigned(followUnrealizedPnl) }}</span>
            </div>
            <div class="quick-actions follow-actions">
              <button class="act-btn act-primary" @click="router.push('/copy-trade')">{{ t('assets.goCopyTrade') }}</button>
              <button class="act-btn act-primary" @click="openTransfer('SPOT', 'FOLLOW')">{{ t('assets.transfer') }}</button>
            </div>
          </div>
          <p class="follow-subtitle">{{ t('assets.followPositions') }}</p>
          <div v-if="followPosLoading" class="follow-loading">{{ t('common.loading') }}</div>
          <div v-else-if="!followPositions.length" class="follow-empty">{{ t('assets.noFollowPositions') }}</div>
          <div v-else class="follow-pos-list">
            <div v-for="p in followPositions" :key="p.id" class="follow-pos-card dark-card">
              <div class="fp-head">
                <span class="fp-sym">{{ fmtFollowSymbol(p.symbol) }}</span>
                <span class="fp-dir" :class="p.direction === 0 ? 'dir-long' : 'dir-short'">
                  {{ p.direction === 0 ? t('assets.long') : t('assets.short') }}
                </span>
              </div>
              <p v-if="p.traderName" class="fp-trader">{{ t('assets.trader') }} {{ p.traderName }}</p>
              <div class="fp-grid">
                <div class="fp-cell">
                  <span class="fp-lbl">{{ t('assets.openPrice') }}</span>
                  <span class="fp-val">{{ obscure ? '****' : fmt(p.openPrice, 4) }}</span>
                </div>
                <div class="fp-cell">
                  <span class="fp-lbl">{{ t('assets.currentPrice') }}</span>
                  <span class="fp-val">{{ obscure ? '****' : fmt(p.closePrice, 4) }}</span>
                </div>
                <div class="fp-cell">
                  <span class="fp-lbl">{{ t('assets.amountLever') }}</span>
                  <span class="fp-val">{{ obscure ? '****' : `${fmt(p.amount, 4)} · ${p.lever}x` }}</span>
                </div>
                <div class="fp-cell fp-cell--pnl">
                  <span class="fp-lbl">{{ t('assets.unrealizedPnl') }}</span>
                  <span class="fp-val" :class="p.profit >= 0 ? 'up' : 'down'">
                    {{ obscure ? '****' : `${fmtSigned(p.profit)} (${fmtSigned(p.profitRate)}%)` }}
                  </span>
                </div>
              </div>
              <button
                v-if="!p.isNoLoss"
                type="button"
                class="fp-close"
                :disabled="!canCloseAssetFollowPosition(p) || followCloseBusyId !== null"
                @click="closeAssetFollowPosition(p)"
              >
                {{ followCloseBusyId === String(p.id) ? t('assets.closing') : t('assets.closePosition') }}
              </button>
            </div>
          </div>
        </template>

        <!-- Tab 5：锁仓账户 -->
        <template v-if="activeTab === ASSET_TABS.STAKING">
          <div class="staking-hero dark-card">
            <p class="label-sm">{{ t('assets.releaseTotal') }}</p>
            <div class="staking-main-balance">
              <span class="staking-main-num">{{ obscure ? '****' : fmt(stakingOverviewReleased) }}</span>
              <span class="unit">{{ t('assets.ibitUnit') }}</span>
            </div>
            <div class="staking-total-row">
              <span>{{ t('assets.totalLockedBalance') }}</span>
              <span class="staking-total-val">
                {{ obscure ? '****' : fmt(Number(stakingOverviewAvailable) + Number(stakingOverviewLocked)) }}
              </span>
            </div>
            <div class="staking-actions">
              <button class="act-btn act-primary" type="button" @click="router.push('/assets/deposit')">{{ t('assets.depositCoin') }}</button>
              <button class="act-btn act-muted" type="button" @click="router.push('/assets/withdraw')">{{ t('assets.withdrawCoin') }}</button>
              <button class="act-btn act-muted" type="button" @click="openTransfer('SPOT', 'SWAP')">{{ t('assets.transfer') }}</button>
            </div>
          </div>

          <div class="staking-orders-section">
            <div class="staking-orders-head">
              <p class="staking-orders-title">{{ t('assets.myStakingOrders') }}</p>
              <select
                v-model="stakingOrderStatusFilter"
                class="staking-status-select"
                :disabled="stakingOrdersLoading"
                @change="changeStakingOrderStatus"
              >
                <option v-for="option in stakingOrderStatusOptions" :key="option.value" :value="option.value">
                  {{ option.label }}
                </option>
              </select>
            </div>
            <div
              class="staking-pull-area"
              @touchstart.passive="beginStakingPull"
              @touchmove.passive="moveStakingPull"
              @touchend="endStakingPull"
              @touchcancel="endStakingPull"
            >
              <div
                class="staking-refresh-hint"
                :style="{ height: `${stakingPullDistance}px`, opacity: stakingPullDistance > 0 || stakingOrdersLoading ? 1 : 0 }"
              >
                {{ stakingRefreshText }}
              </div>
              <div v-if="stakingOrdersLoading && !stakingOrders.length" class="follow-loading">{{ t('common.loading') }}</div>
              <div v-else-if="!stakingOrders.length" class="follow-empty">{{ t('common.noData') }}</div>
              <div v-else class="staking-table-wrap">
                <table class="staking-table">
                  <thead>
                    <tr>
                      <th>{{ t('assets.stakingOrderNo') }}</th>
                      <th>{{ t('assets.purchaseQty') }}</th>
                      <th>{{ t('assets.stakingTotal') }}</th>
                      <th>{{ t('assets.released') }}</th>
                      <th>{{ t('assets.pendingRelease') }}</th>
                      <th>{{ t('assets.startTime') }}</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr v-for="order in stakingOrders" :key="order.id">
                      <td class="staking-td-id">{{ formatStakingOrderId(order) }}</td>
                      <td>{{ formatStakingOrderAmount(order, order.amount) }}</td>
                      <td>{{ formatStakingOrderAmount(order, order.amount) }}</td>
                      <td>{{ formatStakingOrderAmount(order, order.releasedAmount) }}</td>
                      <td>{{ formatStakingOrderAmount(order, stakingPendingAmount(order)) }}</td>
                      <td class="staking-td-time">{{ shortDate(order.stakingTime || order.createTime) }}</td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </template>


        <!-- 底部记录入口 -->
        <div class="footer-links">
          <RouterLink to="/assets/history" class="footer-link">{{ t('assets.assetRecord') }}</RouterLink>
          <RouterLink to="/assets/deposit/history" class="footer-link">{{ t('assets.depositRecord2') }}</RouterLink>
          <RouterLink to="/assets/withdraw/history" class="footer-link">{{ t('assets.withdrawRecord') }}</RouterLink>
          <RouterLink to="/assets/transfer/history" class="footer-link">{{ t('assets.transferRecord') }}</RouterLink>
        </div>
      </template>
    </div>

    <!-- 划转弹窗 -->
    <Teleport to="body">
      <div v-if="showTransfer" class="modal-overlay" @click.self="showTransfer = false">
        <div class="modal dark-card">
          <div class="modal-header">
            <h3 class="modal-title">{{ t('assets.accountTransfer') }}</h3>
            <button class="modal-close" @click="showTransfer = false">✕</button>
          </div>
          <div v-if="isSpotCoinTransfer" class="form-group">
            <label class="form-label">{{ t('spot.transferModal.selectCoin') }}</label>
            <select v-model="transferSymbol" class="form-select" :disabled="transferCoinLoading">
              <option v-for="c in coinOptions" :key="c" :value="c">{{ c }}</option>
            </select>
          </div>
          <div class="tf-row">
            <div class="tf-side">
              <p class="tf-lbl">{{ t('assets.transferFrom') }}</p>
              <div v-if="transferFrom === 'SPOT'" class="form-select form-select--readonly">{{ wLabel('SPOT') }}</div>
              <select v-else v-model="transferFrom" class="form-select">
                <option v-for="walletType in OTHER_WALLET_TYPES" :key="walletType" :value="walletType">{{ wLabel(walletType) }}</option>
              </select>
              <p class="tf-bal">
                {{ t('assets.availableCoin', {
                  available: fmt(transferFromAvailable),
                  coin: isSpotCoinTransfer ? transferSymbol : 'USDT',
                }) }}
              </p>
            </div>
            <button class="swap-btn" @click="swapTransfer">⇄</button>
            <div class="tf-side">
              <p class="tf-lbl">{{ t('assets.transferTo') }}</p>
              <div
                v-if="transferFrom === 'SWAP' || transferFrom === 'FOLLOW' || transferFrom === 'SPOT_TRADING'"
                class="form-select form-select--readonly"
              >
                {{ wLabel('SPOT') }}
              </div>
              <select v-else v-model="transferTo" class="form-select">
                <option v-for="walletType in OTHER_WALLET_TYPES" :key="walletType" :value="walletType">{{ wLabel(walletType) }}</option>
              </select>
            </div>
          </div>
          <div class="form-group">
            <div class="amount-row-hd">
              <label class="form-label">
                {{ isSpotCoinTransfer
                  ? t('assets.amountCoin', { coin: transferSymbol })
                  : t('assets.amountUsdt') }}
              </label>
              <button class="all-btn" type="button" @click="transferAmount = transferFromAvailable">{{ t('common.all') }}</button>
            </div>
            <input v-model="transferAmount" type="number" class="form-input" placeholder="0.00" min="0" />
          </div>
          <p class="arrive-tip">{{ t('assets.freeTransfer') }}</p>
          <p v-if="transferError" class="form-error">{{ transferError }}</p>
          <div class="modal-actions">
            <button class="btn-cancel" type="button" @click="showTransfer = false">{{ t('common.cancel') }}</button>
            <button
              class="btn-confirm"
              type="button"
              :disabled="transferLoading || transferCoinLoading"
              @click="doTransfer"
            >
              {{ transferLoading ? t('assets.transferring') : t('assets.confirmTransferBtn') }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>
  </main>
</template>

<style scoped>
.content {
  padding-top: var(--space-8);
  padding-bottom: var(--space-16);
  max-width: 800px;
}
.page-title {
  font-size: var(--text-h1); font-weight: var(--fw-bold);
  color: var(--color-text-primary); margin-bottom: var(--space-6);
}
.loading-state { display: flex; justify-content: center; padding: var(--space-16); }
.spinner {
  width: 32px; height: 32px; border-radius: 50%;
  border: 2px solid var(--color-border);
  border-top-color: var(--color-primary);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── 总资产头部 ── */
.header-card {
  padding: var(--space-6); margin-bottom: var(--space-5);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-8);
}
.label-sm { font-size: var(--text-small); color: var(--color-text-secondary); margin-bottom: var(--space-2); }
.total-row { display: flex; align-items: baseline; gap: var(--space-2); }
.total-num { font-size: 32px; font-weight: var(--fw-bold); font-family: var(--font-family-num); color: var(--color-text-primary); }
.unit { font-size: var(--text-small); color: var(--color-text-secondary); }
.eye-btn {
  background: none; border: none; cursor: pointer;
  padding: var(--space-1);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--color-text-secondary);
  border-radius: var(--radius-sm);
  transition: color var(--transition);
}
.eye-btn:hover { color: var(--color-text-primary); }
.eye-icon { width: 1.25rem; height: 1.25rem; display: block; flex-shrink: 0; }
.pnl-row { margin-top: var(--space-2); font-size: var(--text-small); display: flex; align-items: center; gap: var(--space-2); }
.pnl-label { color: var(--color-text-secondary); }
.up   { color: var(--color-up); }
.down { color: var(--color-down); }
.header-actions { display: flex; gap: var(--space-3); flex-shrink: 0; }
.act-btn {
  padding: var(--space-2) var(--space-5); border-radius: var(--radius-md);
  font-size: var(--text-small); font-weight: var(--fw-semibold); cursor: pointer;
  background: transparent; border: 1px solid var(--color-border);
  color: var(--color-text-secondary); transition: all var(--transition);
}
.act-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
.act-btn.act-primary { background: var(--color-primary); border-color: var(--color-primary); color: #000; }
.act-btn.act-primary:hover { opacity: .9; }
.act-btn.act-outline {
  background: transparent;
  border: 1px solid var(--color-primary);
  color: var(--color-primary);
}
.act-btn.act-outline:hover { opacity: .85; }

/* ── Tab ── */
.tab-bar { display: flex; border-bottom: 1px solid var(--color-border); margin-bottom: var(--space-5); }
.tab-item {
  padding: var(--space-3) var(--space-5); background: none; border: none;
  color: var(--color-text-secondary); font-size: var(--text-body); cursor: pointer;
  border-bottom: 2px solid transparent; margin-bottom: -1px; transition: all var(--transition);
}
.tab-item.active { color: var(--color-primary); border-bottom-color: var(--color-primary); }
.tab-item:hover { color: var(--color-text-primary); }

/* ── 账户列表 ── */
.account-list { display: flex; flex-direction: column; gap: var(--space-3); }
.account-row {
  display: flex; align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-6);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}
.account-row:hover,
.account-row:focus-visible {
  border-color: rgba(240,185,11,.45);
  background: var(--color-bg-card-hover);
  outline: none;
}
.acc-left { display: flex; align-items: center; gap: var(--space-3); }
.acc-icon { font-size: 20px; }
.acc-label { font-size: var(--text-body); font-weight: var(--fw-medium); color: var(--color-text-primary); }
.acc-right { display: flex; align-items: center; gap: var(--space-2); }
.acc-balance { font-size: var(--text-body); font-weight: var(--fw-semibold); font-family: var(--font-family-num); color: var(--color-text-primary); }
.acc-unit { font-size: var(--text-small); color: var(--color-text-secondary); }
.mini-btn {
  margin-left: var(--space-3); padding: 2px var(--space-3);
  background: transparent; border: 1px solid var(--color-primary);
  border-radius: var(--radius-sm); color: var(--color-primary);
  font-size: var(--text-caption); cursor: pointer;
}

/* ── 详情卡 ── */
.detail-card { padding: var(--space-6); }
.detail-header { font-size: var(--text-body); font-weight: var(--fw-semibold); color: var(--color-text-primary); margin-bottom: var(--space-5); }
.swap-equity-block { margin-bottom: var(--space-4); }
.swap-equity-block .label-sm { margin-bottom: var(--space-2); }
.swap-equity-block .total-row { margin-top: 0; }
.detail-row {
  display: flex; justify-content: space-between;
  padding: var(--space-3) 0; border-bottom: 1px solid var(--color-border);
}
.detail-row:last-of-type { border-bottom: none; }
.detail-label { font-size: var(--text-small); color: var(--color-text-secondary); }
.detail-val { font-size: var(--text-small); color: var(--color-text-primary); font-family: var(--font-family-num); }
.quick-actions { display: flex; gap: var(--space-3); margin-top: var(--space-5); }
.fund-sum-actions {
  flex-wrap: wrap;
  max-width: 280px;
}
.fund-sum-actions .act-btn {
  flex: 1 1 auto;
  min-width: 72px;
  justify-content: center;
}
.staking-summary .spot-sum-actions {
  padding-top: 0;
  align-self: center;
}
.follow-actions .act-btn { flex: 1; }

.act-btn.act-muted {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.12);
  color: var(--color-text-secondary);
}
.act-btn.act-muted:hover {
  border-color: rgba(255, 255, 255, 0.22);
  color: var(--color-text-primary);
}

/* ── 锁仓 Tab ── */
.staking-hero {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-6);
  margin-bottom: var(--space-5);
}
.staking-main-balance {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
}
.staking-main-num {
  font-size: 32px;
  font-weight: var(--fw-bold);
  color: var(--color-text-primary);
  font-family: var(--font-family-num);
  line-height: 1.2;
}
.staking-main-balance .unit {
  font-size: var(--text-body);
  color: var(--color-text-secondary);
}
.staking-total-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-6);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text-secondary);
  font-size: var(--text-small);
}
.staking-total-val {
  color: var(--color-text-primary);
  font-family: var(--font-family-num);
  font-weight: var(--fw-medium);
}
.staking-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
.staking-actions .act-btn {
  min-width: 88px;
  padding: var(--space-2) var(--space-6);
}
.staking-orders-section {
  margin-top: var(--space-2);
}
.staking-orders-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.staking-orders-title {
  font-size: var(--text-h3);
  font-weight: var(--fw-semibold);
  color: var(--color-text-primary);
  margin: 0;
}
.staking-status-select {
  min-width: 104px;
  min-height: 34px;
  padding: 0 var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg-input);
  color: var(--color-text-primary);
  font-size: var(--text-small);
  outline: none;
}
.staking-status-select:disabled {
  opacity: .55;
}
.staking-status-select option {
  background: var(--color-bg-secondary);
}
.staking-pull-area {
  touch-action: pan-y;
}
.staking-refresh-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  color: var(--color-text-secondary);
  font-size: var(--text-small);
  transition: height .18s ease, opacity .18s ease;
}
.staking-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.staking-table {
  width: 100%;
  min-width: 720px;
  border-collapse: collapse;
  font-size: var(--text-small);
}
.staking-table th {
  padding: var(--space-3) var(--space-4) var(--space-4);
  font-weight: var(--fw-normal);
  font-size: var(--text-caption);
  color: var(--color-text-muted);
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid var(--color-border);
}
.staking-table td {
  padding: var(--space-5) var(--space-4);
  color: var(--color-text-primary);
  font-family: var(--font-family-num);
  white-space: nowrap;
  vertical-align: middle;
}
.staking-table tbody tr:last-child td {
  padding-bottom: var(--space-2);
}
.staking-td-id {
  font-weight: var(--fw-medium);
  font-family: var(--font-family);
}
.staking-td-time {
  color: var(--color-text-secondary);
  font-family: var(--font-family);
}

/* ── 跟单 Tab 仓位列表 ── */
.follow-subtitle {
  font-size: var(--text-body);
  font-weight: var(--fw-semibold);
  color: var(--color-text-primary);
  margin: var(--space-5) 0 var(--space-3);
}
.follow-loading,
.follow-empty {
  text-align: center;
  padding: var(--space-8) 0;
  color: var(--color-text-secondary);
  font-size: var(--text-small);
}
.follow-pos-list { display: flex; flex-direction: column; gap: var(--space-3); }
.follow-pos-card { padding: var(--space-5) var(--space-6); }
.fp-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-2);
}
.fp-sym { font-weight: var(--fw-semibold); color: var(--color-text-primary); }
.fp-dir {
  font-size: var(--text-caption);
  font-weight: var(--fw-semibold);
  padding: 2px 8px;
  border-radius: var(--radius-sm);
}
.fp-dir.dir-long { color: var(--color-up); background: rgba(14, 203, 129, 0.12); }
.fp-dir.dir-short { color: var(--color-down); background: rgba(246, 70, 93, 0.12); }
.fp-trader { font-size: var(--text-caption); color: var(--color-text-secondary); margin: 0 0 var(--space-3); }
.fp-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3) var(--space-4);
}
.fp-cell { display: flex; flex-direction: column; gap: 2px; }
.fp-cell--pnl { grid-column: 1 / -1; }
.fp-lbl { font-size: var(--text-caption); color: var(--color-text-secondary); }
.fp-val { font-size: var(--text-small); font-weight: var(--fw-medium); font-family: var(--font-family-num); color: var(--color-text-primary); }
.fp-close {
  margin-top: var(--space-4);
  width: 100%;
  padding: var(--space-2);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text-secondary);
  font-size: var(--text-small);
  cursor: pointer;
}
.fp-close:hover:not(:disabled) { border-color: var(--color-primary); color: var(--color-primary); }
.fp-close:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── 底部链接 ── */
.footer-links { display: flex; gap: var(--space-5); margin-top: var(--space-5); justify-content: center; }
.footer-link { font-size: var(--text-small); color: var(--color-text-secondary); text-decoration: none; }
.footer-link:hover { color: var(--color-primary); }

/* ── 划转弹窗 ── */
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); display: flex; align-items: center; justify-content: center; z-index: 100; }
.modal { width: 460px; max-width: calc(100vw - var(--space-8)); padding: var(--space-6); }
.modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-6); }
.modal-title { font-size: var(--text-h3); font-weight: var(--fw-semibold); color: var(--color-text-primary); }
.modal-close { background: none; border: none; color: var(--color-text-muted); font-size: 18px; cursor: pointer; }
.tf-row { display: flex; align-items: flex-start; gap: var(--space-3); margin-bottom: var(--space-5); }
.tf-side { flex: 1; }
.tf-lbl { font-size: var(--text-caption); color: var(--color-text-secondary); margin-bottom: var(--space-2); }
.tf-bal { font-size: var(--text-caption); color: var(--color-text-muted); margin-top: var(--space-2); }
.swap-btn { margin-top: 24px; background: var(--color-bg-card); border: 1px solid var(--color-border); border-radius: 50%; width: 34px; height: 34px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--color-text-secondary); font-size: 18px; flex-shrink: 0; }
.swap-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
.form-select {
  width: 100%; background: var(--color-bg-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-md); padding: var(--space-3) var(--space-4);
  color: var(--color-text-primary); font-size: var(--text-body); outline: none; box-sizing: border-box;
}
.form-select--readonly { cursor: default; color: var(--color-text-secondary); }
.form-select option { background: #1a1a1a; }
.form-group { margin-bottom: var(--space-4); }
.form-label { font-size: var(--text-small); color: var(--color-text-secondary); }
.amount-row-hd { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-2); }
.all-btn { background: none; border: none; color: var(--color-primary); font-size: var(--text-small); cursor: pointer; }
.form-input {
  width: 100%; box-sizing: border-box; background: var(--color-bg-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); color: var(--color-text-primary); outline: none;
}
.form-input:focus { border-color: var(--color-primary); }
.arrive-tip { font-size: var(--text-caption); color: var(--color-text-muted); margin-bottom: var(--space-4); }
.form-error { font-size: var(--text-small); color: var(--color-down); margin-bottom: var(--space-4); }
.modal-actions { display: flex; gap: var(--space-3); margin-top: var(--space-5); }
.btn-cancel { flex: 1; padding: var(--space-3); background: var(--color-bg-card); border: 1px solid var(--color-border); border-radius: var(--radius-md); color: var(--color-text-secondary); cursor: pointer; }
.btn-confirm { flex: 2; padding: var(--space-3); background: var(--color-primary); border: none; border-radius: var(--radius-md); color: #000; font-weight: var(--fw-semibold); cursor: pointer; }
.btn-confirm:disabled { opacity: .5; cursor: not-allowed; }

/* ── 现货账户 ── */
.spot-summary {
  padding: var(--space-6); margin-bottom: var(--space-5);
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-6);
}
.spot-sum-left { flex: 1; min-width: 0; }
.spot-sum-actions { display: flex; gap: var(--space-3); flex-shrink: 0; align-items: flex-start; padding-top: var(--space-3); }
.spot-list-toolbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  margin: var(--space-2) 0 var(--space-4);
}

.hide-zero-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0;
  border: none;
  background: transparent;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.5);
  cursor: pointer;
  user-select: none;
  transition: color 0.2s;
}

.hide-zero-toggle:hover {
  color: rgba(255, 255, 255, 0.75);
}

.hide-zero-toggle.active {
  color: rgba(255, 255, 255, 0.85);
}

.hide-zero-check {
  width: 16px;
  height: 16px;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 4px;
  flex-shrink: 0;
  position: relative;
  transition: background 0.2s, border-color 0.2s;
}

.hide-zero-toggle.active .hide-zero-check {
  background: var(--color-primary);
  border-color: var(--color-primary);
}

.hide-zero-toggle.active .hide-zero-check::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid #000;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.empty-spot { text-align: center; padding: var(--space-10) 0; color: var(--color-text-secondary); font-size: var(--text-small); }
.spot-wallet-list { display: flex; flex-direction: column; gap: var(--space-3); }
.spot-wallet-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) var(--space-6); gap: var(--space-4);
}
.sw-coin { display: flex; align-items: center; gap: var(--space-3); min-width: 80px; }
.sw-icon {
  width: 36px; height: 36px; border-radius: 50%; overflow: hidden;
  background: var(--color-bg-card); display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.sw-icon img { width: 100%; height: 100%; object-fit: cover; }
.sw-icon-fb { font-size: 13px; font-weight: 700; color: var(--color-primary); }
.sw-name { font-size: var(--text-body); font-weight: var(--fw-semibold); color: var(--color-text-primary); }
.sw-amounts { display: flex; gap: var(--space-6); }
.sw-cell { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.sw-lbl { font-size: var(--text-caption); color: var(--color-text-secondary); }
.sw-val { font-size: var(--text-small); font-weight: var(--fw-semibold); font-family: var(--font-family-num); color: var(--color-text-primary); }

@media (max-width: 768px) {
  .header-card { flex-direction: column; }
  .total-num { font-size: 24px; }
  .spot-summary { flex-direction: column; }
  .sw-amounts { gap: var(--space-4); }
}
</style>
