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

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

export type StakeChain = 'ETH' | 'SOL'

/** Десятичность смолл-юнитов: ETH = wei (10^18), SOL = lamports (10^9). */
export const STAKE_DECIMALS: Record<StakeChain, number> = { ETH: 18, SOL: 9 }

// ── Низкоуровневые запросы (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() }
}

// ── 1.1 Quote ──

interface EthQuoteRaw {
  amountWeiHuman: string
  appFeeWeiHuman: string
  stakeAmountWeiHuman: string
  expectedStEthWeiHuman: string
  aprPercent: number | null
}

interface SolQuoteRaw {
  amountLamportsHuman: string
  appFeeLamportsHuman: string
  stakeLamportsHuman: string
  validator: string
  estApyPercent: number | null
}

/** Нормализованное превью стейка для UI (human-числа). */
export interface StakeQuote {
  chain: StakeChain
  amountHuman: string
  feeHuman: string
  afterFeeHuman: string
  /** Ожидаемый stETH (ETH) либо делегируемая сумма (SOL). */
  resultHuman: string
  aprPercent: number | null
  validator?: string
}

export async function getStakeQuote(chain: StakeChain, amountHuman: string): Promise<StakeQuote> {
  const amount = toBaseUnits(amountHuman, STAKE_DECIMALS[chain])

  if (chain === 'ETH') {
    const res = await walletPost<{ success: boolean; data: EthQuoteRaw }>('/api/wallets/ETH/stake/quote', { amount })
    const d = res.data
    return {
      chain,
      amountHuman: d.amountWeiHuman,
      feeHuman: d.appFeeWeiHuman,
      afterFeeHuman: d.stakeAmountWeiHuman,
      resultHuman: d.expectedStEthWeiHuman,
      aprPercent: d.aprPercent,
    }
  }

  const res = await walletPost<{ success: boolean; data: SolQuoteRaw }>('/api/wallets/SOL/stake/quote', { amount })
  const d = res.data
  return {
    chain,
    amountHuman: d.amountLamportsHuman,
    feeHuman: d.appFeeLamportsHuman,
    afterFeeHuman: d.stakeLamportsHuman,
    resultHuman: d.stakeLamportsHuman,
    aprPercent: d.estApyPercent,
    validator: d.validator,
  }
}

// ── 1.2 Stake ──

export async function stake(chain: StakeChain, amountHuman: string): Promise<void> {
  const amount = toBaseUnits(amountHuman, STAKE_DECIMALS[chain])
  await walletPost(`/api/wallets/${chain}/stake`, { amount }, true, idempotency())
}

// ── 1.3 Positions ──

export interface EthWithdrawalRequest {
  requestId: string
  amountStEthWeiHuman: string
  isFinalized: boolean
  isClaimed: boolean
}

export interface EthStakePositions {
  chain: 'ETH'
  protocol: string
  stEthBalanceWeiHuman: string
  aprPercent: number | null
  withdrawalRequests: EthWithdrawalRequest[]
  /** Клиентский флаг: позиция показана оптимистично и ещё не подтверждена сервером. */
  optimistic?: boolean
}

export type SolState = 'active' | 'activating' | 'deactivating' | 'inactive'

export interface SolPosition {
  stakeAccount: string
  lamportsHuman: string
  state: SolState
  validator: string
  /** Клиентский флаг: позиция показана оптимистично и ещё не подтверждена сервером. */
  optimistic?: boolean
}

export interface SolStakePositions {
  chain: 'SOL'
  protocol: string
  validator: string
  positions: SolPosition[]
}

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

export async function getSolPositions(): Promise<SolStakePositions> {
  const res = await walletGet<{ success: boolean; data: SolStakePositions }>('/api/wallets/SOL/stake/positions')
  return res.data
}

// ── 1.4 Unstake ──

export interface EthUnstakeResult {
  mode: 'swap' | 'queue'
  note?: string
}

export async function unstakeEth(amountHuman: string, mode: 'swap' | 'queue'): Promise<EthUnstakeResult> {
  const amount = toBaseUnits(amountHuman, STAKE_DECIMALS.ETH)
  const res = await walletPost<{ success: boolean; data: EthUnstakeResult }>(
    '/api/wallets/ETH/unstake',
    { amount, mode },
    true,
    idempotency()
  )
  return res.data
}

export interface SolUnstakeResult {
  action: 'deactivate' | 'withdraw'
  note?: string
}

export async function unstakeSol(stakeAccount: string): Promise<SolUnstakeResult> {
  const res = await walletPost<{ success: boolean; data: SolUnstakeResult }>(
    '/api/wallets/SOL/unstake',
    { stakeAccount },
    true,
    idempotency()
  )
  return res.data
}

// ── 1.5 Claim (ETH, mode=queue) ──

export async function claimUnstake(requestId: string): Promise<void> {
  await walletPost('/api/wallets/ETH/unstake/claim', { requestId }, true, idempotency())
}

// ── Предварительная проверка стоимости/баланса (op-cost) ──
// Read-only: считает комиссию 0.7% (в токене + нативе + USD) и проверяет, хватает ли
// средств на сумму И нативки на газ. Для нативного стейкинга token = символ сети.

export interface OpCostLeg {
  symbol?: string
  have?: string | number
  need?: string | number
  needReserve?: string | number
  shortfall?: string | number
  sufficient?: boolean | null
}

export interface OpCost {
  appCommission?: {
    ratePercent?: number
    inToken?: { symbol: string; amount: string }
    inNative?: { symbol: string; amount: string }
    usd?: number | null
  }
  balance?: { token?: OpCostLeg; gas?: OpCostLeg }
  /** Хватает ли средств на сумму + газ. */
  sufficient: boolean
}

export async function getOpCost(chain: StakeChain, amountHuman: string): Promise<OpCost> {
  const res = await walletPost<{ success: boolean; data: OpCost }>(`/api/wallets/${chain}/op-cost`, {
    token: chain,
    amountHuman,
  })
  return res.data
}

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

/** Достаёт человекочитаемый текст из конверта ошибки {success:false,error,code} или Error. */
export function getStakingErrorMessage(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 'Произошла ошибка. Попробуйте ещё раз.'
}
