import { useState, useCallback, useEffect, useRef } from 'react';
import {
  deriveAddresses,
  getBalance,
  getUtxos,
  getTxHistory,
  estimateFee,
  getNetworkInfo,
  getBlockCount,
  getTxStatus,
  listWallets,
  loadWallet,
  setLabel,
  getUtxoTree,
  type AddressInfo,
  type WalletMeta,
  type TxInfo,
  type FeeEstimate,
} from '../api';

// Fiat price caching
let btcPrice = 0;
let btcPriceTime = 0;

export async function fetchBtcPrice(): Promise<number> {
  const now = Date.now();
  if (btcPrice && now - btcPriceTime < 60_000) return btcPrice;

  try {
    const resp = await fetch('https://api.coindesk.com/v1/bpi/currentprice.json');
    const data = await resp.json();
    btcPrice = data.bpi.USD.rate_float;
    btcPriceTime = now;
    return btcPrice;
  } catch {
    return btcPrice || 0;
  }
}

export function btcToFiat(btc: number): number {
  return btc * btcPrice;
}

export function useWallet() {
  const [currentWallet, setCurrentWallet] = useState<any>(null);
  const [walletPassword, setWalletPassword] = useState('');
  const [addresses, setAddresses] = useState<AddressInfo[]>([]);
  const [balance, setBalance] = useState<{ balance_btc: number; balance_sats: number } | null>(null);
  const [utxos, setUtxos] = useState<any[]>([]);
  const [txHistory, setTxHistory] = useState<TxInfo[]>([]);
  const [feeEstimate, setFeeEstimate] = useState<FeeEstimate | null>(null);
  const [networkInfo, setNetworkInfo] = useState<any>(null);
  const [blockCount, setBlockCount] = useState<number | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [savedWallets, setSavedWallets] = useState<WalletMeta[]>([]);
  const [addrLabels, setAddrLabels] = useState<Record<string, string>>({});
  const [utxoTree, setUtxoTree] = useState<any[]>([]);

  // Get address strings for API calls
  const addrStrings = addresses.map(a => a.address);

  const refreshBalance = useCallback(async () => {
    if (addrStrings.length === 0) return;
    try {
      const bal = await getBalance(addrStrings);
      setBalance(bal);
    } catch (e: any) {
      setError(e.message || 'Failed to fetch balance');
    }
  }, [addrStrings]);

  const refreshUtxos = useCallback(async () => {
    if (addrStrings.length === 0) return;
    try {
      const list = await getUtxos(addrStrings);
      setUtxos(list);
    } catch (e: any) {
      setError(e.message || 'Failed to fetch UTXOs');
    }
  }, [addrStrings]);

  const refreshHistory = useCallback(async () => {
    if (!currentWallet) return;
    try {
      const allTxs: TxInfo[] = [];
      for (const addr of addrStrings.slice(0, 5)) {
        const txs = await getTxHistory(addr, 10);
        const txids = new Set(allTxs.map(t => t.txid));
        for (const tx of txs) {
          if (!txids.has(tx.txid)) {
            allTxs.push(tx);
            txids.add(tx.txid);
          }
        }
      }
      allTxs.sort((a, b) => (b.time || 0) - (a.time || 0));
      setTxHistory(allTxs);
    } catch (e: any) {
      setError(e.message || 'Failed to fetch history');
    }
  }, [currentWallet, addrStrings]);

  const refreshFee = useCallback(async () => {
    try {
      const fee = await estimateFee(6);
      setFeeEstimate(fee);
    } catch {
      // ignore
    }
  }, []);

  const refreshNetwork = useCallback(async () => {
    try {
      const net = await getNetworkInfo();
      setNetworkInfo(net);
    } catch {
      // ignore
    }
  }, []);

  const refreshBlockCount = useCallback(async () => {
    try {
      const bc = await getBlockCount();
      setBlockCount(bc);
    } catch {
      // ignore
    }
  }, []);

  const refreshAll = useCallback(async () => {
    if (loading) return;
    setLoading(true);
    setError('');
    try {
      await Promise.all([
        refreshBalance(),
        refreshUtxos(),
        refreshHistory(),
        refreshFee(),
        refreshNetwork(),
        refreshBlockCount(),
        (async () => {
          await fetchBtcPrice();
        })(),
      ]);
    } catch (e: any) {
      setError(e.message || 'Refresh failed');
    } finally {
      setLoading(false);
    }
  }, [loading, refreshBalance, refreshUtxos, refreshHistory, refreshFee, refreshNetwork, refreshBlockCount]);

  // Auto-refresh on wallet change
  useEffect(() => {
    if (currentWallet) {
      refreshAll();
      // Load saved wallets list
      listWallets().then(r => setSavedWallets(r.wallets || [])).catch(() => {});
    }
  }, [currentWallet?.file_path]);

  // Periodic refresh
  useEffect(() => {
    if (!currentWallet) return;
    const interval = setInterval(refreshAll, 60_000);
    return () => clearInterval(interval);
  }, [currentWallet?.file_path, refreshAll]);

  const refreshTxStatus = useCallback(async (txid: string) => {
    try {
      return await getTxStatus(txid);
    } catch {
      return { txid, confirmations: 0, status: 'not_found' as const };
    }
  }, []);

  // Switch to a new wallet (from creation flow)
  const switchWallet = useCallback((wallet: any, addrs: AddressInfo[]) => {
    setCurrentWallet(wallet);
    setAddresses(addrs);
  }, []);

  // Load wallet from disk (sidebar selection)
  const loadWalletFromDisk = useCallback(async (path: string) => {
    setLoading(true);
    setError('');
    try {
      const w = await loadWallet(path, '');
      const addrs = await deriveAddresses(w.wallet, 5);
      setCurrentWallet(w.wallet);
      setAddresses(addrs);
      setAddrLabels({});
    } catch (e: any) {
      setError(e.message || 'Failed to load wallet');
    } finally {
      setLoading(false);
    }
  }, []);

  // Derive more addresses
  const deriveMore = useCallback(async (num: number = 5): Promise<AddressInfo[]> => {
    if (!currentWallet) return [];
    setLoading(true);
    try {
      const newAddrs = await deriveAddresses(currentWallet, num, 'bech32', walletPassword);
      setAddresses(prev => [...prev, ...newAddrs]);
      return newAddrs;
    } catch (e: any) {
      setError(e.message || 'Failed to derive addresses');
      return [];
    } finally {
      setLoading(false);
    }
  }, [currentWallet, walletPassword]);

  // Set label
  const setLabelFn = useCallback(async (kind: string, identifier: string, label: string) => {
    if (!currentWallet) return;
    try {
      await setLabel(currentWallet, kind, identifier, label);
      if (kind === 'addr') {
        setAddrLabels(prev => ({ ...prev, [identifier]: label }));
      }
    } catch (e: any) {
      setError(e.message || 'Failed to set label');
    }
  }, [currentWallet]);

  // UTXO tree
  const fetchUtxoTree = useCallback(async () => {
    try {
      const tree = await getUtxoTree(utxos, txHistory);
      setUtxoTree(tree);
    } catch {
      // ignore
    }
  }, [utxos, txHistory]);

  return {
    currentWallet,
    walletPassword,
    setWalletPassword,
    addresses,
    balance,
    utxos,
    txHistory,
    feeEstimate,
    networkInfo,
    blockCount,
    loading,
    error,
    savedWallets,
    addrLabels,
    utxoTree,
    refreshAll,
    refreshTxStatus,
    switchWallet,
    loadWalletFromDisk,
    deriveMore,
    setLabel: setLabelFn,
    fetchUtxoTree,
    setError,
  };
}