import { PrimaryButton, TokenIcon } from '@shared/ui'
import { COIN_ICONS } from '@shared/assets/coins'
import { truncateDecimals } from '@shared/lib/utils/truncateDecimals'
import { toBaseUnits, fromBaseUnits } from '@shared/lib/utils/baseUnits'
import { useDebounce } from '@shared/lib/hooks/useDebounce'
import { useWalletBalance } from '@features/wallet'
import { useStakeQuote, useOpCost, STAKE_DECIMALS, type StakeChain, type StakeQuote, type OpCost } from '@features/staking'
import { StakeQuotePanel, type QuoteRow } from './StakeQuotePanel'
import { CHAIN_META } from '../model/meta'
import styles from './StakeCard.module.css'

interface Props {
  chain: StakeChain
  amount: string
  onAmountChange: (value: string) => void
  onStake: (stakeAmountHuman: string) => void
  isStaking: boolean
}

const PERCENTS = [25, 50, 100]

type Meta = (typeof CHAIN_META)[StakeChain]

const DASH = '—'

function usd(value: number): string {
  return `≈ $${value.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
}

// Сумма в поле ввода (T) — это сумма к списанию с кошелька. Из неё резервируется газ (G),
// в стейкинг уходит A = T − G, и уже на A сервер считает комиссию 0.7% и итоговый stETH.
function buildRows(
  meta: Meta,
  quote: StakeQuote | undefined,
  opCost: OpCost | undefined,
  totalNum: number,
  nativeUsdPrice: number
): QuoteRow[] {
  const isEth = meta.symbol === 'ETH'
  const aprLabel = `Годовая доходность (${meta.aprLabel})`

  // Сервисный сбор (0.7%) показываем в долларах из op-cost (appCommission.usd), а не в крипте.
  const feeUsd = opCost?.appCommission?.usd
  const fee = feeUsd != null ? usd(feeUsd) : DASH
  const apr = quote?.aprPercent != null ? `${quote.aprPercent}%` : DASH

  const resultLabel = isEth ? 'Вы получите' : 'Делегируется валидатору'
  const result = quote
    ? isEth
      ? `≈ ${truncateDecimals(quote.resultHuman, 8)} stETH`
      : `${truncateDecimals(quote.resultHuman, 8)} SOL`
    : DASH

  const gasRaw = opCost?.balance?.gas?.needReserve
  const gasNum = gasRaw != null ? parseFloat(String(gasRaw)) || 0 : 0
  const gas = gasRaw != null ? `≈ ${truncateDecimals(String(gasRaw), 8)} ${meta.symbol}` : DASH

  // Сумма, реально уходящая в стейкинг (до комиссии сервиса): T − газ.
  const stakeNum = Math.max(0, totalNum - gasNum)
  const stakeAmount = gasRaw != null && totalNum > 0 ? `≈ ${truncateDecimals(stakeNum.toString(), 8)} ${meta.symbol}` : DASH

  // Газ уже внутри суммы к списанию, поэтому итог = T в USD.
  const totalUsd = nativeUsdPrice > 0 && totalNum > 0 ? usd(totalNum * nativeUsdPrice) : DASH

  return [
    { label: 'Сетевой сбор (газ)', value: gas },
    { label: 'Сумма в стейкинг', value: stakeAmount },
    { label: 'Комиссия сервиса (0.7%)', value: fee },
    { label: aprLabel, value: apr, accent: 'success' },
    { label: 'Итого к списанию', value: totalUsd },
    { label: resultLabel, value: result, accent: 'highlight', emphasis: true },
  ]
}

export function StakeCard({ chain, amount, onAmountChange, onStake, isStaking }: Props) {
  const meta = CHAIN_META[chain]
  const decimals = STAKE_DECIMALS[chain]
  const { data: balance } = useWalletBalance(chain)
  const balanceHuman = balance?.native.formatted ?? '0'
  const balanceNum = parseFloat(balanceHuman) || 0

  const debounced = useDebounce(amount, 400)
  const { data: opCost, isError: opError } = useOpCost(chain, debounced)

  // Газ за операцию резервируется в том же нативном токене (op-cost → balance.gas.needReserve).
  const gasReserveHuman = opCost?.balance?.gas?.needReserve != null ? String(opCost.balance.gas.needReserve) : null

  // Сумма к стейку A = T − газ. Считаем в смолл-юнитах через BigInt (без float-погрешностей).
  // Газ неизвестен: при ошибке op-cost отдаём всю сумму (бэкенд провалидирует), пока грузится — 0.
  function computeStakeAmount(totalHuman: string): string {
    const totalNum = parseFloat(totalHuman) || 0
    if (totalNum <= 0) return '0'
    if (gasReserveHuman != null) {
      const totalBase = BigInt(toBaseUnits(totalHuman, decimals))
      const gasBase = BigInt(toBaseUnits(gasReserveHuman, decimals))
      const stakeBase = totalBase > gasBase ? totalBase - gasBase : 0n
      return fromBaseUnits(stakeBase.toString(), decimals)
    }
    return opError ? totalHuman : '0'
  }

  const stakeAmountHuman = computeStakeAmount(amount)
  const stakeAmountDebounced = computeStakeAmount(debounced)
  const stakeAmountNum = parseFloat(stakeAmountHuman) || 0

  // Котировку (комиссия, итог, stETH) считаем на сумму стейка A, а не на сумму к списанию T.
  const { data: quote, isFetching } = useStakeQuote(chain, stakeAmountDebounced)

  const amountNum = parseFloat(amount) || 0
  const withinBalance = amountNum > 0 && amountNum <= balanceNum
  // Сумма к списанию меньше сетевого сбора — стейкать нечего.
  const belowGas = withinBalance && gasReserveHuman != null && stakeAmountNum <= 0
  const canStake = withinBalance && stakeAmountNum > 0 && !isStaking

  const setPercent = (p: number) => {
    // МАКС/100% = весь баланс; поле НЕ пересчитывается на газ — газ вычитается ниже, внутри A.
    if (p >= 100) onAmountChange(balanceHuman)
    else onAmountChange(((balanceNum * p) / 100).toString())
  }

  const pills = (
    <>
      {PERCENTS.map((p) => (
        <button key={p} className={styles.pill} onClick={() => setPercent(p)} aria-label={`${p}% от баланса`}>
          {p}%
        </button>
      ))}
    </>
  )

  return (
    <div className={styles.wrapper}>
      <div className={styles.pillsOuter}>{pills}</div>

      <div className={styles.card}>
        <div className={styles.top}>
          <span className={styles.tag}>Сумма к списанию</span>
          <div className={styles.right}>
            <span className={styles.apr}>
              {meta.aprLabel} {quote?.aprPercent != null ? `${quote.aprPercent}%` : '—'}
            </span>
            <div className={styles.pillsInner}>{pills}</div>
          </div>
        </div>

        <div className={styles.mid}>
          <input
            className={styles.input}
            type="text"
            inputMode="decimal"
            value={amount}
            onChange={(e) => {
              const v = e.target.value
              if (/^(\d+\.?\d*|\.?\d*)$/.test(v) || v === '') onAmountChange(v)
            }}
            placeholder="0"
            aria-label={`Сумма к списанию в ${meta.symbol}`}
          />
          <div className={styles.token}>
            <TokenIcon letter={meta.symbol[0]} color="var(--grad-center)" logo={COIN_ICONS[meta.symbol]} size={28} />
            <span>{meta.symbol}</span>
          </div>
        </div>

        <div className={styles.bottom}>
          <span className={styles.balance}>
            Баланс: {truncateDecimals(balanceHuman, 6)} {meta.symbol}
            <button className={styles.max} onClick={() => setPercent(100)} aria-label="Использовать весь баланс">
              МАКС
            </button>
          </span>
        </div>

        <StakeQuotePanel
          rows={buildRows(meta, quote, opCost, amountNum, balance?.native.usdPrice ?? 0)}
          loading={isFetching}
        />

        {belowGas && <p className={styles.hint}>Сумма меньше сетевого сбора (газ).</p>}

        <div className={`${styles.stakeBtn} ${!canStake ? styles.stakeBtnDisabled : ''}`}>
          <PrimaryButton
            label={isStaking ? 'Отправка…' : 'Застейкать'}
            type="button"
            onClick={() => onStake(stakeAmountHuman)}
            disabled={!canStake}
          />
        </div>
      </div>
    </div>
  )
}
