import { getCsrfToken } from '@shared/api/csrf'
import { tokenStore, refreshAccessToken } from '@shared/api/tokenStore'
import {
  getOrganizationWallets,
  getOrganizationBalances,
  type OrgAmountRaw,
  type OrgWalletBalance,
} from '@features/b2b'

const WALLET_API_URL = 'https://app.cryptowallet.elcsa.ru'

export type Chain = 'ETH' | 'BSC' | 'BTC' | 'TRX' | 'SOL'

export interface FormattedAmount {
  raw: string
  formatted: string
  decimals: number
  usdPrice: number
  usdValue: number
}

export interface WalletBalanceData {
  chain: Chain
  address: string
  native: FormattedAmount
  tokens: Record<string, FormattedAmount>
}

export interface PriceEntry {
  usd: number
}

export interface SendWalletPayload {
  to: string
  amount: string
  token?: string
  feeTier?: 'slow' | 'normal' | 'fast'
}

export interface SendWalletResponse {
  data: { txid: string; chain: Chain }
}

export interface WalletAddress {
  chain: Chain
  address: string
  derivationPath: string
}

export interface PortfolioChain {
  chain: Chain
  address: string
  native: FormattedAmount
  tokens: Record<string, FormattedAmount>
  totalUsd: number
  stale: boolean
  lastUpdated: number
}

export interface PortfolioData {
  totalUsd: number
  hasErrors: boolean
  perChain: Record<Chain, PortfolioChain>
}

export const CHAINS: Chain[] = ['ETH', 'BSC', 'BTC', 'TRX', 'SOL']

async function walletGet<T>(path: string, allowRetry: boolean = 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: boolean = 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
}

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

export async function getWalletBalance(chain: Chain): Promise<WalletBalanceData> {
  const res = await walletGet<{ success: boolean; data: WalletBalanceData }>(`/api/wallets/${chain}/balance`)
  return res.data
}

export async function getPrices(symbols: string[]): Promise<Record<string, PriceEntry>> {
  const res = await walletGet<{ success: boolean; data: Record<string, PriceEntry> }>(
    `/api/prices?symbols=${symbols.join(',')}`
  )
  return res.data
}

export async function sendWallet(chain: Chain, payload: SendWalletPayload): Promise<SendWalletResponse> {
  return walletPost<SendWalletResponse>(`/api/wallets/${chain}/send`, payload)
}

export async function getPortfolio(): Promise<PortfolioData> {
  const res = await walletGet<{ success: boolean; data: PortfolioData }>('/api/wallets/portfolio')
  return res.data
}

// --- Legal-entity (b2b organizations) adapters ---
// The b2b balances endpoint returns the same information as the cryptowallet
// portfolio, but with snake_case keys, string-encoded numbers and an items[]
// array instead of a perChain map. These adapters normalise it into the
// existing PortfolioData / WalletAddress shapes so the wallet UI is reused as-is.

const NATIVE_SYMBOL_TO_CHAIN: Record<string, Chain> = {
  ETH: 'ETH',
  BNB: 'BSC',
  BTC: 'BTC',
  TRX: 'TRX',
  SOL: 'SOL',
}

/** Normalise a b2b chain/native_symbol into our Chain enum, or null if unknown. */
function normalizeChain(chain: string, nativeSymbol?: string): Chain | null {
  const upper = chain?.toUpperCase()
  if ((CHAINS as string[]).includes(upper)) return upper as Chain
  const bySymbol = nativeSymbol ? NATIVE_SYMBOL_TO_CHAIN[nativeSymbol.toUpperCase()] : undefined
  return bySymbol ?? null
}

function parseNum(value: string | number | null | undefined): number {
  const n = typeof value === 'number' ? value : parseFloat(value ?? '')
  return Number.isFinite(n) ? n : 0
}

function toFormattedAmount(a: OrgAmountRaw): FormattedAmount {
  return {
    raw: a.raw,
    formatted: a.formatted,
    decimals: a.decimals,
    usdPrice: parseNum(a.usd_price),
    usdValue: parseNum(a.usd_value),
  }
}

function toPortfolioChain(item: OrgWalletBalance, chain: Chain): PortfolioChain {
  const tokens: Record<string, FormattedAmount> = {}
  for (const [symbol, amount] of Object.entries(item.tokens ?? {})) {
    tokens[symbol] = toFormattedAmount(amount)
  }
  return {
    chain,
    address: item.address,
    native: toFormattedAmount(item.native),
    tokens,
    totalUsd: parseNum(item.total_usd),
    stale: false,
    lastUpdated: 0,
  }
}

export async function getOrgPortfolio(): Promise<PortfolioData> {
  const res = await getOrganizationBalances()
  const perChain = {} as Record<Chain, PortfolioChain>
  for (const item of res.items ?? []) {
    const chain = normalizeChain(item.chain, item.native_symbol)
    if (!chain) continue
    perChain[chain] = toPortfolioChain(item, chain)
  }
  return {
    totalUsd: parseNum(res.total_usd),
    hasErrors: !!res.has_errors,
    perChain,
  }
}

const EMPTY_AMOUNT: FormattedAmount = { raw: '0', formatted: '0', decimals: 0, usdPrice: 0, usdValue: 0 }

export async function getOrgWalletBalance(chain: Chain): Promise<WalletBalanceData> {
  const portfolio = await getOrgPortfolio()
  const c = portfolio.perChain[chain]
  if (!c) return { chain, address: '', native: EMPTY_AMOUNT, tokens: {} }
  return { chain, address: c.address, native: c.native, tokens: c.tokens }
}

export async function getOrgWalletAddresses(): Promise<WalletAddress[]> {
  const wallets = await getOrganizationWallets()
  const result: WalletAddress[] = []
  for (const w of wallets ?? []) {
    const chain = normalizeChain(w.chain)
    if (!chain) continue
    result.push({ chain, address: w.address, derivationPath: w.derivation_path })
  }
  return result
}

export interface TokenInfo {
  chain: string
  symbol: string
  name: string
  contract: string | null
}

export interface RelayQuotePayload {
  user: string
  recipient: string
  originChainId: number
  destinationChainId: number
  originCurrency: string
  destinationCurrency: string
  amount: string
  tradeType: 'EXACT_INPUT'
}

export interface RelayQuoteResponse {
  details: {
    currencyOut: {
      amountFormatted: string
      amountUsd: string
    }
  }
  fees: {
    gas: {
      amountUsd: string
    }
  }
  appCommission?: {
    ratePercent: number
    inToken: { symbol: string; amount: string }
    usd: number
  }
}

export async function getTokensList(): Promise<TokenInfo[]> {
  const res = await walletGet<{ success: boolean; data: TokenInfo[] }>('/api/tokens')
  return res.data
}

export interface JumperToken {
  address: string
  chainId: number
  symbol: string
  decimals: number
  name: string
  coinKey?: string
  logoURI?: string
  priceUSD: string
}

export type JumperTokensMap = Record<string, JumperToken[]>

export async function getJumperTokens(): Promise<JumperTokensMap> {
  const res = await walletGet<{ tokens?: JumperTokensMap; data?: { tokens: JumperTokensMap } }>(
    '/api/jumper/tokens?chains=1,56,1151111081099710,728126428,20000000000001'
  )
  return res.data?.tokens ?? res.tokens ?? {}
}

export interface JumperQuotePayload {
  fromChain: string
  toChain: string
  fromToken: string
  toToken: string
  fromAmount: string
  fromAddress: string
  toAddress: string
  slippage: number
}

export interface JumperQuoteToken {
  address: string
  chainId: number
  symbol: string
  decimals: number
  name: string
  logoURI?: string
  priceUSD: string
}

export interface JumperFeeCost {
  name: string
  description?: string
  token: JumperQuoteToken
  amount: string
  amountUSD: string
  percentage?: string
  included?: boolean
}

export interface JumperQuote {
  type: string
  id: string
  tool: string
  toolDetails: { key: string; name: string; logoURI?: string }
  action: {
    fromToken: JumperQuoteToken
    fromAmount: string
    toToken: JumperQuoteToken
    fromChainId: number
    toChainId: number
    slippage: number
    fromAddress: string
    toAddress: string
  }
  estimate: {
    tool: string
    approvalAddress?: string
    toAmountMin: string
    toAmount: string
    fromAmount: string
    feeCosts?: JumperFeeCost[]
  }
}

export async function getJumperQuote(payload: JumperQuotePayload): Promise<JumperQuote> {
  const qs = new URLSearchParams({
    fromChain: payload.fromChain,
    toChain: payload.toChain,
    fromToken: payload.fromToken,
    toToken: payload.toToken,
    fromAmount: payload.fromAmount,
    fromAddress: payload.fromAddress,
    toAddress: payload.toAddress,
    slippage: String(payload.slippage),
  }).toString()
  const res = await walletGet<JumperQuote & { body?: JumperQuote; data?: { body?: JumperQuote } }>(
    `/api/jumper/quote-best?${qs}`
  )
  return (res.data?.body ?? res.body ?? res) as JumperQuote
}

export interface BridgeExecutePayload {
  provider: string
  fromChain: number
  toChain: number
  fromToken: string
  toToken: string
  fromAmount: string
  fromAddress: string
  toAddress: string
  acceptedMinOut?: string
}

export interface BridgeExecuteResult {
  provider: string
  fromChain: number
  toChain: number
  toolName: string
  feeTxid?: string
  feeAmount?: string
  bridgeTxid: string
  fromAmount: string
  toAmountMin: string
  fromAmountUSD?: string
  toAmountUSD?: string
  trackerUrl?: string
}

export async function executeBridge(payload: BridgeExecutePayload): Promise<BridgeExecuteResult> {
  const res = await walletPost<{ data?: { success: boolean; data: BridgeExecuteResult } }>(
    '/api/bridge/execute',
    payload,
    true,
    { 'Idempotency-Key': crypto.randomUUID() }
  )
  return (res.data?.data ?? res) as BridgeExecuteResult
}

export async function getRelayQuote(payload: RelayQuotePayload): Promise<RelayQuoteResponse> {
  return walletPost<RelayQuoteResponse>('/api/relay/quote', payload)
}

export interface RelaySwapStep {
  id: string
  action: string
  description: string
  kind: string
  items: Array<{
    status: string
    data: {
      from: string
      to: string
      data: string
      value: string
      chainId: number
      gas: string
      maxFeePerGas: string
      maxPriorityFeePerGas: string
    }
    check: {
      endpoint: string
      method: string
    }
  }>
  requestId: string
}

export interface RelaySwapResponse {
  steps: RelaySwapStep[]
  fees: RelayQuoteResponse['fees']
  details: {
    operation: string
    sender: string
    recipient: string
    currencyIn: { amount: string; amountFormatted: string; amountUsd: string; currency: { symbol: string } }
    currencyOut: { amount: string; amountFormatted: string; amountUsd: string; currency: { symbol: string } }
    totalImpact: { usd: string; percent: string }
    rate: string
    timeEstimate: number
  }
}

export async function executeRelaySwap(payload: RelayQuotePayload): Promise<RelaySwapResponse> {
  return walletPost<RelaySwapResponse>('/api/relay/execute/swap', payload)
}

export async function signRawEvmTx(
  chain: 'ETH' | 'BSC',
  txData: RelaySwapStep['items'][0]['data']
): Promise<unknown> {
  const key = `relay-${chain.toLowerCase()}-${Date.now()}`
  return walletPost(`/api/wallets/${chain}/sign-raw-evm-tx`, txData, true, { 'Idempotency-Key': key })
}

export async function signSolTx(txData: unknown): Promise<unknown> {
  return walletPost('/api/wallets/SOL/sign-and-broadcast-tx', txData)
}

export interface TrxSwapQuotePayload {
  from: string
  to: string
  amountHuman: string
}

export interface TrxSwapQuoteData {
  quoteId: string
  expiresIn: number
  expectedOutFormatted: string
  minOutFormatted: string
  fees: {
    network: { amountFormatted: string; asset: string; amountUsd: number }
  }
}

export async function getTrxSwapQuote(payload: TrxSwapQuotePayload): Promise<TrxSwapQuoteData> {
  const res = await walletPost<{ success: boolean; data: TrxSwapQuoteData }>(
    '/api/wallets/TRX/swap/quote',
    payload
  )
  return res.data
}

export async function executeTrxSwap(quoteId: string): Promise<unknown> {
  return walletPost(
    '/api/wallets/TRX/swap',
    { quoteId },
    true,
    { 'Idempotency-Key': `trx-${Date.now()}` }
  )
}

export async function createWallet(): Promise<void> {
  await walletPost<unknown>('/api/wallets/create', {})
}

export async function revealMnemonic(): Promise<string> {
  const res = await walletPost<{ success: boolean; data: { mnemonic: string } }>('/api/wallets/mnemonic/reveal', { confirm: 'I_UNDERSTAND_SEED_IS_SECRET' })
  return res.data.mnemonic
}
