"use client";

import { createContext, useCallback, useContext, useEffect, useMemo, useState, ReactNode } from "react";
import type { VaultReceipt, VaultState } from "@/lib/metaGame";
import { addKey, addReceipt, defaultVaultState, loadVaultState, saveVaultState, setFlag } from "@/lib/metaGame";
import { getLedgerRow, migrateLegacyDeviceLedgerIfNeeded, setLedgerRow } from "@/lib/storeCreditStorage";
import { useAccount } from "@/contexts/AccountContext";

interface WalletContextType {
  usdStoreCredit: number;
  luxCredits: number;
  spendLuxCredits: (amount: number) => boolean;
  earnLuxCredits: (amount: number) => void;
  spendUsdStoreCredit: (amount: number) => boolean;
  verifyBtcDeposit: (txid: string) => Promise<{ ok: boolean; error?: string; creditedUsd?: number }>;
  creditBtcPayInvoice: (invoiceId: string, usdAmount: number) => { ok: boolean; error?: string };
  vault: VaultState;
  collectKey: (key: string) => boolean;
  addVaultReceipt: (receipt: VaultReceipt) => void;
  setVaultFlag: (flag: string, value: boolean) => void;
}

const WalletContext = createContext<WalletContextType | undefined>(undefined);

export const useWallet = () => {
  const context = useContext(WalletContext);
  if (!context) {
    throw new Error("useWallet must be used within WalletProvider");
  }
  return context;
};

interface WalletProviderProps {
  children: ReactNode;
}

export const WalletProvider = ({ children }: WalletProviderProps) => {
  const { user } = useAccount();
  const handle = user?.username ?? null;

  const [usdStoreCredit, setUsdStoreCredit] = useState(0);
  const [luxCredits, setLuxCredits] = useState(0);
  const [vault, setVault] = useState<VaultState>(() => defaultVaultState());

  useEffect(() => {
    if (!handle) {
      setUsdStoreCredit(0);
      setLuxCredits(0);
      return;
    }
    migrateLegacyDeviceLedgerIfNeeded(handle);
    const row = getLedgerRow(handle);
    setUsdStoreCredit(row.usd);
    setLuxCredits(row.lux);
  }, [handle]);

  useEffect(() => {
    setVault(loadVaultState(handle));
  }, [handle]);

  useEffect(() => {
    saveVaultState(handle, vault);
  }, [vault, handle]);

  const spendLuxCredits = useCallback((amount: number) => {
    if (!handle) return false;
    const safeAmount = Number.isFinite(amount) ? Math.max(0, Math.floor(amount)) : 0;
    if (safeAmount <= 0) return true;
    const row = getLedgerRow(handle);
    if (row.lux < safeAmount) return false;
    const nextLux = row.lux - safeAmount;
    setLedgerRow(handle, { usd: row.usd, lux: nextLux, claimedTxids: row.claimedTxids });
    setLuxCredits(nextLux);
    return true;
  }, [handle]);

  const earnLuxCredits = useCallback(
    (amount: number) => {
      if (!handle) return;
      const safeAmount = Number.isFinite(amount) ? Math.max(0, Math.floor(amount)) : 0;
      if (safeAmount <= 0) return;
      const row = getLedgerRow(handle);
      const nextLux = row.lux + safeAmount;
      setLedgerRow(handle, { usd: row.usd, lux: nextLux, claimedTxids: row.claimedTxids });
      setLuxCredits(nextLux);
    },
    [handle],
  );

  const spendUsdStoreCredit = useCallback(
    (amount: number) => {
      if (!handle) return false;
      const safe = Number.isFinite(amount) ? Math.max(0, Math.round(amount * 100) / 100) : 0;
      if (safe <= 0) return true;
      const row = getLedgerRow(handle);
      if (row.usd + 1e-9 < safe) return false;
      const nextUsd = Math.round((row.usd - safe) * 100) / 100;
      setLedgerRow(handle, { usd: nextUsd, lux: row.lux, claimedTxids: row.claimedTxids });
      setUsdStoreCredit(nextUsd);
      return true;
    },
    [handle],
  );

  const verifyBtcDeposit = useCallback(
    async (txid: string) => {
      if (!handle) {
        return { ok: false, error: "Sign in so deposits credit your handle’s balance." };
      }
      const normalized = String(txid || "").trim();
      if (!/^[a-fA-F0-9]{64}$/.test(normalized)) {
        return { ok: false, error: "Enter a valid 64-character transaction id" };
      }
      const row = getLedgerRow(handle);
      if (row.claimedTxids.includes(normalized)) {
        return { ok: false, error: "This transaction was already used to add credit" };
      }
      try {
        const res = await fetch("/api/btc/verify", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ txid: normalized }),
        });
        const data = (await res.json()) as {
          ok?: boolean;
          error?: string;
          creditedUsd?: number;
        };
        if (!data.ok || typeof data.creditedUsd !== "number") {
          return { ok: false, error: data.error || "Verification failed" };
        }
        const add = Math.round(data.creditedUsd * 100) / 100;
        const nextUsd = Math.round((row.usd + add) * 100) / 100;
        const claimed = [...row.claimedTxids, normalized];
        setLedgerRow(handle, { usd: nextUsd, lux: row.lux, claimedTxids: claimed });
        setUsdStoreCredit(nextUsd);
        return { ok: true, creditedUsd: add };
      } catch {
        return { ok: false, error: "Network error while verifying" };
      }
    },
    [handle],
  );

  const creditBtcPayInvoice = useCallback(
    (invoiceId: string, usdAmount: number) => {
      if (!handle) return { ok: false, error: "Sign in first" };
      const id = String(invoiceId || "").trim();
      if (!id) return { ok: false, error: "Invalid invoice ID" };
      const safe = Number.isFinite(usdAmount) ? Math.max(0, Math.round(usdAmount * 100) / 100) : 0;
      if (safe <= 0) return { ok: false, error: "Invalid amount" };
      const row = getLedgerRow(handle);
      if (row.claimedTxids.includes(id)) return { ok: false, error: "Invoice already credited" };
      const nextUsd = Math.round((row.usd + safe) * 100) / 100;
      setLedgerRow(handle, { usd: nextUsd, lux: row.lux, claimedTxids: [...row.claimedTxids, id] });
      setUsdStoreCredit(nextUsd);
      return { ok: true };
    },
    [handle],
  );

  const collectKey = (key: string) => {
    const trimmed = String(key || "").trim();
    if (!trimmed) return false;
    if (vault.keys.includes(trimmed)) return false;
    setVault((v) => addKey(v, trimmed));
    return true;
  };

  const addVaultReceipt = (receipt: VaultReceipt) => {
    setVault((v) => addReceipt(v, receipt));
  };

  const setVaultFlag = (flag: string, value: boolean) => {
    const f = String(flag || "").trim();
    if (!f) return;
    setVault((v) => setFlag(v, f, Boolean(value)));
  };

  const value = useMemo(
    () => ({
      usdStoreCredit,
      luxCredits,
      spendLuxCredits,
      earnLuxCredits,
      spendUsdStoreCredit,
      verifyBtcDeposit,
      creditBtcPayInvoice,
      vault,
      collectKey,
      addVaultReceipt,
      setVaultFlag,
    }),
    [
      usdStoreCredit,
      luxCredits,
      spendLuxCredits,
      earnLuxCredits,
      spendUsdStoreCredit,
      verifyBtcDeposit,
      creditBtcPayInvoice,
      vault,
    ],
  );

  return <WalletContext.Provider value={value}>{children}</WalletContext.Provider>;
};
