import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { toBaseUnits, fromBaseUnits } from '@shared/lib/utils/baseUnits'
import {
  getStakeQuote,
  getOpCost,
  getEthPositions,
  getSolPositions,
  stake,
  unstakeEth,
  unstakeSol,
  claimUnstake,
  STAKE_DECIMALS,
  type StakeChain,
  type EthStakePositions,
  type SolStakePositions,
  type SolPosition,
  type EthUnstakeResult,
  type SolUnstakeResult,
} from '../api/stakingApi'

type Positions = EthStakePositions | SolStakePositions

/** Оптимистично добавляет только что застейканную сумму в текущие позиции. */
function addOptimisticPosition(prev: Positions, amountHuman: string): Positions {
  if (prev.chain === 'ETH') {
    const base = BigInt(toBaseUnits(prev.stEthBalanceWeiHuman, STAKE_DECIMALS.ETH))
    const add = BigInt(toBaseUnits(amountHuman, STAKE_DECIMALS.ETH))
    return {
      ...prev,
      stEthBalanceWeiHuman: fromBaseUnits((base + add).toString(), STAKE_DECIMALS.ETH),
      optimistic: true,
    }
  }
  const optimistic: SolPosition = {
    stakeAccount: 'pending',
    lamportsHuman: amountHuman,
    state: 'activating',
    validator: prev.validator,
    optimistic: true,
  }
  return { ...prev, positions: [optimistic, ...prev.positions] }
}

export function useStakeQuote(chain: StakeChain, amountHuman: string) {
  const amount = parseFloat(amountHuman)
  return useQuery({
    queryKey: ['staking', 'quote', chain, amountHuman],
    queryFn: () => getStakeQuote(chain, amountHuman),
    enabled: Number.isFinite(amount) && amount > 0,
    staleTime: 15_000,
    retry: false,
  })
}

export function useOpCost(chain: StakeChain, amountHuman: string) {
  const amount = parseFloat(amountHuman)
  return useQuery({
    queryKey: ['staking', 'op-cost', chain, amountHuman],
    queryFn: () => getOpCost(chain, amountHuman),
    enabled: Number.isFinite(amount) && amount > 0,
    staleTime: 15_000,
    retry: false,
  })
}

export function useStakePositions(chain: StakeChain) {
  return useQuery<EthStakePositions | SolStakePositions>({
    queryKey: ['staking', 'positions', chain],
    queryFn: () => (chain === 'ETH' ? getEthPositions() : getSolPositions()),
    staleTime: 30_000,
    retry: false,
  })
}

export function useStake() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: ({ chain, amountHuman }: { chain: StakeChain; amountHuman: string }) => stake(chain, amountHuman),
    // Рисуем позицию сразу, оптимистично — не ждём подтверждения.
    onMutate: async ({ chain, amountHuman }) => {
      const key = ['staking', 'positions', chain] as const
      // Отменяем летящий рефетч, чтобы он не затёр оптимистичную позицию.
      await qc.cancelQueries({ queryKey: key })
      const previous = qc.getQueryData<Positions>(key)
      if (previous) qc.setQueryData(key, addOptimisticPosition(previous, amountHuman))
      return { key, previous }
    },
    // Стейк не прошёл — откатываем оптимистичную позицию (ошибку покажет тост в виджете).
    onError: (_err, _vars, ctx) => {
      if (ctx?.previous) qc.setQueryData(ctx.key, ctx.previous)
    },
    onSettled: (_data, _err, { chain }) => {
      // Баланс обновляем сразу; позиции сверяем с сервером с задержкой — стейк отражается в
      // сети не мгновенно, а ранний рефетч мигнул бы (позиции ещё нет → исчезла бы → вернулась).
      // Если этот рефетч упадёт, PositionsPanel скроет данные и покажет ошибку.
      qc.invalidateQueries({ queryKey: ['wallet', 'balance', chain] })
      setTimeout(() => {
        qc.invalidateQueries({ queryKey: ['staking', 'positions', chain] })
        qc.invalidateQueries({ queryKey: ['wallet', 'balance', chain] })
      }, 6000)
    },
  })
}

export type UnstakeVars =
  | { chain: 'ETH'; amountHuman: string; mode: 'swap' | 'queue' }
  | { chain: 'SOL'; stakeAccount: string }

export function useUnstake() {
  const qc = useQueryClient()
  return useMutation<EthUnstakeResult | SolUnstakeResult, Error, UnstakeVars>({
    mutationFn: (vars) =>
      vars.chain === 'ETH' ? unstakeEth(vars.amountHuman, vars.mode) : unstakeSol(vars.stakeAccount),
    onSuccess: (_data, vars) => {
      qc.invalidateQueries({ queryKey: ['staking', 'positions', vars.chain] })
      // Монеты вернулись на кошелёк — обновляем баланс (prefix-match покрывает org/self).
      qc.invalidateQueries({ queryKey: ['wallet', 'balance', vars.chain] })
    },
  })
}

export function useClaim() {
  const qc = useQueryClient()
  return useMutation({
    mutationFn: (requestId: string) => claimUnstake(requestId),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['staking', 'positions', 'ETH'] })
      qc.invalidateQueries({ queryKey: ['wallet', 'balance', 'ETH'] })
    },
  })
}
