import { getCsrfToken } from '@shared/api/csrf'
import { tokenStore, refreshAccessToken } from '@shared/api/tokenStore'
import { toBaseUnits } from '@shared/lib/utils/baseUnits'

// Пулы живут на том же custodial-хосте, что и кошелёк/стейкинг (см. features/staking/api/stakingApi.ts).
const WALLET_API_URL = 'https://app.cryptowallet.elcsa.ru'

// ── Низкоуровневые запросы (CSRF + Bearer + один silent refresh на 401) ──

async function walletGet<T>(path: string, allowRetry = true): Promise<T> {
  const csrf = await getCsrfToken()
  const bearer = tokenStore.get()

  const res = await fetch(`${WALLET_API_URL}${path}`, {
    credentials: 'include',
    headers: {
      'X-CSRF-Token': csrf,
      ...(bearer ? { Authorization: `Bearer ${bearer}` } : {}),
    },
  })

  if (res.status === 401 && allowRetry) {
    try {
      await refreshAccessToken()
      return walletGet<T>(path, false)
    } catch {
      tokenStore.clear()
      throw new Error('Unauthorized')
    }
  }

  const data = await res.json()
  if (!res.ok) throw data
  return data as T
}

async function walletPost<T>(
  path: string,
  body: unknown,
  allowRetry = true,
  extraHeaders: Record<string, string> = {}
): Promise<T> {
  const csrf = await getCsrfToken()
  const bearer = tokenStore.get()

  const res = await fetch(`${WALLET_API_URL}${path}`, {
    method: 'POST',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': csrf,
      ...(bearer ? { Authorization: `Bearer ${bearer}` } : {}),
      ...extraHeaders,
    },
    body: JSON.stringify(body),
  })

  if (res.status === 401 && allowRetry) {
    try {
      await refreshAccessToken()
      return walletPost<T>(path, body, false, extraHeaders)
    } catch {
      tokenStore.clear()
      throw new Error('Unauthorized')
    }
  }

  const data = await res.json()
  if (!res.ok) throw data
  return data as T
}

/** Idempotency-Key для мутирующих операций (анти-дабл-спенд). */
function idempotency(): Record<string, string> {
  return { 'Idempotency-Key': crypto.randomUUID() }
}

// ── 2.1 Список курируемых пулов ──

export interface Pool {
  poolAddress: string
  token0: string
  token1: string
  symbol0: string
  symbol1: string
  decimals0: number
  decimals1: number
  /** В сотых долях процента: 3000 = 0.3%, 500 = 0.05%. */
  feeTier: number
  currentPrice: number
  currentTick: number
  /** Внешняя статистика может быть null, если недоступна (не блокирует депозит). */
  tvlUSD: number | null
  volumeUSD24h: number | null
  aprPercent: number | null
}

export async function getPools(): Promise<Pool[]> {
  const res = await walletGet<{ success: boolean; data: Pool[] }>('/api/wallets/ETH/lp/pools')
  return res.data
}

// ── 2.2 Превью депозита (комиссия 0.7% + проверка баланса) ──

/** Нога баланса: sufficient может быть null (не проверялось / покрыто нативным ETH). */
export interface LpBalanceLeg {
  symbol: string
  have: string
  need: string
  sufficient: boolean | null
  shortfall?: string
}

export interface LpGasLeg {
  symbol: string
  have: string
  needReserve: string
  sufficient: boolean
}

export interface LpQuote {
  poolAddress: string
  tickLower: number
  tickUpper: number
  priceLower: number
  priceUpper: number
  amount0DesiredHuman: string
  amount1DesiredHuman: string
  amount0MinHuman: string
  amount1MinHuman: string
  liquidity: string
  aprPercent: number | null
  appFee0Human: string
  appFee1Human: string
  appFee0Usd: number | null
  appFee1Usd: number | null
  appFeeTotalUsd: number | null
  appFeeTotalEth: string | null
  /** Присутствует, если у юзера есть ETH-кошелёк (всегда у custodial-юзера). */
  balance?: {
    token0: LpBalanceLeg
    token1: LpBalanceLeg
    gas: LpGasLeg
    sufficient: boolean
  }
}

/** UI-facing вход для quote/add: human-суммы + границы диапазона. */
export interface LpDepositInput {
  pool: Pool
  amount0Human: string
  amount1Human: string
  priceLower: number
  priceUpper: number
  /** 0..5000, дефолт 100 (= 1%). */
  slippageBps?: number
  /** Завернуть ETH→WETH, если нога пула — WETH, а на балансе нативный ETH. */
  useNativeEth?: boolean
}

/** Собирает тело запроса (доку 2.2): human-суммы → smallest units по decimals каждого токена. */
function buildLpBody(input: LpDepositInput): Record<string, unknown> {
  const { pool, amount0Human, amount1Human, priceLower, priceUpper, slippageBps, useNativeEth } = input
  return {
    pool: pool.poolAddress,
    priceLower,
    priceUpper,
    amount0Desired: toBaseUnits(amount0Human || '0', pool.decimals0),
    amount1Desired: toBaseUnits(amount1Human || '0', pool.decimals1),
    ...(slippageBps != null ? { slippageBps } : {}),
    ...(useNativeEth != null ? { useNativeEth } : {}),
  }
}

export async function getLpQuote(input: LpDepositInput): Promise<LpQuote> {
  const res = await walletPost<{ success: boolean; data: LpQuote }>('/api/wallets/ETH/lp/quote', buildLpBody(input))
  return res.data
}

// ── 2.3 Депозит ──

export interface AddLpResult {
  feeTxids: string[]
  approveTxids: string[]
  mintTxid: string
  wrapTxid?: string
  tickLower: number
  tickUpper: number
  appFee0Human: string
  appFee1Human: string
}

export async function addLp(input: LpDepositInput): Promise<AddLpResult> {
  const res = await walletPost<{ success: boolean; data: AddLpResult }>(
    '/api/wallets/ETH/lp/add',
    buildLpBody(input),
    true,
    idempotency()
  )
  return res.data
}

// ── 2.4 Вывод ──

export interface RemoveLpResult {
  removeTxid: string
  removedLiquidity: string
  burned: boolean
}

export async function removeLp(tokenId: string, percent = 100): Promise<RemoveLpResult> {
  const res = await walletPost<{ success: boolean; data: RemoveLpResult }>(
    '/api/wallets/ETH/lp/remove',
    { tokenId, percent },
    true,
    idempotency()
  )
  return res.data
}

// ── 2.5 Позиции юзера ──

export interface LpPosition {
  tokenId: string
  poolAddress: string
  symbol0: string
  symbol1: string
  feeTier: number
  tickLower: number
  tickUpper: number
  priceLower: number
  priceUpper: number
  liquidity: string
  currentAmount0Human: string
  currentAmount1Human: string
  feesOwed0Human: string
  feesOwed1Human: string
  /** false → цена вышла за диапазон, позиция временно не зарабатывает комиссии. */
  inRange: boolean
}

export interface LpPositions {
  chain: 'ETH'
  positions: LpPosition[]
  totalNfts: number
  scanned: number
  truncated: boolean
}

export async function getLpPositions(): Promise<LpPositions> {
  const res = await walletGet<{ success: boolean; data: LpPositions }>('/api/wallets/ETH/lp/positions')
  return res.data
}

// ── Ошибки ──

/** Достаёт человекочитаемый текст из конверта ошибки {success:false,error,code} или Error. */
export function getPoolsErrorMessage(e: unknown): string {
  if (e && typeof e === 'object' && 'error' in e) {
    const msg = (e as { error?: unknown }).error
    if (typeof msg === 'string') return msg
  }
  if (e instanceof Error) {
    return e.message === 'Unauthorized' ? 'Сессия истекла — перезагрузите страницу.' : e.message
  }
  return 'Произошла ошибка. Попробуйте ещё раз.'
}
