import { useState, useEffect, useCallback } from 'react';
import type { OAuthSession } from '@atproto/oauth-client-browser';
import { getOAuthClient, login } from '../lib/oauth';
import { getBalance, getUtxos, submitTransactionWithSession, formatToshis, parseToshis, MICRO, resolveHandle } from '../lib/api';
import type { Balance, UTXO, TransactionResult } from '../lib/api';
import { Stack, Cluster, Divider } from './ui/Layout';
import {
  Terminal,
  Section,
  LabelValue,
  DataTable,
  DataRow,
  ActionBar,
  InputField,
  Message,
} from './ui/Terminal';

type View = 'main' | 'send' | 'burn' | 'history';

interface WalletState {
  loading: boolean;
  error: string | null;
  session: OAuthSession | null;
  balance: Balance | null;
  utxos: UTXO[];
}

export default function Wallet() {
  const [state, setState] = useState<WalletState>({
    loading: true,
    error: null,
    session: null,
    balance: null,
    utxos: [],
  });
  const [view, setView] = useState<View>('main');
  const [handle, setHandle] = useState('');
  const [connecting, setConnecting] = useState(false);

  useEffect(() => {
    async function init() {
      try {
        const client = getOAuthClient();
        const result = await client.init();

        if (result?.session) {
          setState(s => ({ ...s, session: result.session, loading: false }));
        } else {
          setState(s => ({ ...s, loading: false }));
        }
      } catch (err) {
        setState(s => ({
          ...s,
          loading: false,
          error: err instanceof Error ? err.message : 'Failed to initialize',
        }));
      }
    }
    init();
  }, []);

  useEffect(() => {
    async function fetchData() {
      if (!state.session) return;

      try {
        const did = state.session.did;
        const [balanceRes, utxosRes] = await Promise.all([
          getBalance(did),
          getUtxos(did, 100),
        ]);
        setState(s => ({
          ...s,
          balance: balanceRes,
          utxos: utxosRes.utxos,
        }));
      } catch (err) {
        setState(s => ({
          ...s,
          error: err instanceof Error ? err.message : 'Failed to fetch balance',
        }));
      }
    }
    fetchData();
  }, [state.session]);

  const handleConnect = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!handle.trim()) return;

    setConnecting(true);
    try {
      await login(handle.trim());
    } catch (err) {
      setState(s => ({
        ...s,
        error: err instanceof Error ? err.message : 'Failed to connect',
      }));
      setConnecting(false);
    }
  };

  const handleDisconnect = async () => {
    try {
      if (state.session) {
        await state.session.signOut();
      }
    } catch (e) {
      console.error('Sign out error:', e);
    }
    setState(s => ({ ...s, session: null, balance: null, utxos: [] }));
    window.location.reload();
  };

  const refreshData = useCallback(async () => {
    if (!state.session) return;

    try {
      const did = state.session.did;
      const [balanceRes, utxosRes] = await Promise.all([
        getBalance(did),
        getUtxos(did, 100),
      ]);
      setState(s => ({
        ...s,
        balance: balanceRes,
        utxos: utxosRes.utxos,
        error: null,
      }));
    } catch (err) {
      setState(s => ({
        ...s,
        error: err instanceof Error ? err.message : 'Failed to refresh',
      }));
    }
  }, [state.session]);

  if (state.loading) {
    return (
      <Terminal subtitle="WALLET" showBack>
        <Section>
          <LabelValue label="STATUS" value="LOADING..." status="warning" />
        </Section>
      </Terminal>
    );
  }

  if (!state.session) {
    return (
      <Terminal subtitle="WALLET" showBack>
        <Section title="CONNECT WITH BLUESKY">
          <p className="text-muted">Enter your handle to connect your wallet.</p>
        </Section>

        <Divider />

        <Section>
          <form onSubmit={handleConnect}>
            <Stack gap="md">
              <InputField
                label="HANDLE"
                value={handle}
                onChange={setHandle}
                placeholder="alice.bsky.social"
                disabled={connecting}
                width="260px"
              />
              <Cluster gap="sm">
                <button type="submit" disabled={connecting || !handle.trim()}>
                  {connecting ? '[CONNECTING...]' : '[CONNECT]'}
                </button>
                <a href="/" className="btn secondary">[BACK]</a>
              </Cluster>
            </Stack>
          </form>
        </Section>
      </Terminal>
    );
  }

  return (
    <Terminal title="@toshi WALLET">
      <WalletInfo
        session={state.session}
        balance={state.balance}
        error={state.error}
      />

      <Divider />

      {view === 'main' && (
        <MainView
          utxos={state.utxos}
          onSend={() => setView('send')}
          onBurn={() => setView('burn')}
          onHistory={() => setView('history')}
          onRefresh={refreshData}
        />
      )}
      {view === 'send' && (
        <SendView
          session={state.session}
          utxos={state.utxos}
          onBack={() => { setView('main'); refreshData(); }}
        />
      )}
      {view === 'burn' && (
        <BurnView
          session={state.session}
          utxos={state.utxos}
          onBack={() => { setView('main'); refreshData(); }}
        />
      )}
      {view === 'history' && (
        <HistoryView
          utxos={state.utxos}
          onBack={() => setView('main')}
        />
      )}

      <Divider />

      <ActionBar>
        <button onClick={handleDisconnect} className="secondary">[DISCONNECT]</button>
        <a href="/explorer" className="btn">[EXPLORER]</a>
        <a href="/whitepaper" className="btn secondary">[WHITEPAPER]</a>
      </ActionBar>
    </Terminal>
  );
}

function WalletInfo({ session, balance, error }: {
  session: OAuthSession;
  balance: Balance | null;
  error: string | null;
}) {
  return (
    <Section title="ACCOUNT">
      <Stack gap="xs">
        <LabelValue label="DID" value={session.did} />
        <LabelValue
          label="BALANCE"
          value={`${balance ? formatToshis(balance.balance) : '—'} @toshis`}
        />
        <LabelValue
          label="UTXOS"
          value={balance ? balance.utxoCount : '—'}
        />
      </Stack>
      {error && (
        <Message type="error">ERROR: {error}</Message>
      )}
    </Section>
  );
}

function MainView({ utxos, onSend, onBurn, onHistory, onRefresh }: {
  utxos: UTXO[];
  onSend: () => void;
  onBurn: () => void;
  onHistory: () => void;
  onRefresh: () => void;
}) {
  return (
    <>
      <Section title="COINS">
        <DataTable
          headers={['#', 'TX', 'AMOUNT']}
          widths={['2rem', '1fr', '6rem']}
          emptyMessage="(no coins)"
        >
          {utxos.slice(0, 10).map((utxo, i) => (
            <DataRow
              key={`${utxo.txId}:${utxo.index}`}
              cells={[
                String(i + 1),
                `${utxo.txId}`,
                `${formatToshis(utxo.amount)}`,
              ]}
              widths={['2rem', '1fr', '6rem']}
              href={`/tx/${utxo.txId}`}
            />
          ))}
        </DataTable>
        {utxos.length > 10 && (
          <p className="text-muted">... and {utxos.length - 10} more</p>
        )}
      </Section>

      <Section title="ACTIONS">
        <Cluster gap="sm">
          <button onClick={onSend}>[SEND]</button>
          <button onClick={onBurn} className="secondary">[BURN]</button>
          <button onClick={onHistory} className="secondary">[HISTORY]</button>
          <button onClick={onRefresh} className="secondary">[REFRESH]</button>
        </Cluster>
      </Section>
    </>
  );
}

function SendView({ session, utxos, onBack }: {
  session: OAuthSession;
  utxos: UTXO[];
  onBack: () => void;
}) {
  const [recipients, setRecipients] = useState([{ address: '', amount: '' }]);
  const [sending, setSending] = useState(false);
  const [result, setResult] = useState<TransactionResult | null>(null);
  const [error, setError] = useState<string | null>(null);

  const totalBalanceMicro = utxos.reduce((sum, u) => sum + u.amount, 0);
  const totalBalanceToshis = totalBalanceMicro / MICRO;

  const totalSendToshis = recipients.reduce((sum, r) => sum + (parseFloat(r.amount) || 0), 0);
  const totalSendMicro = Math.round(totalSendToshis * MICRO);

  const addRecipient = () => {
    setRecipients([...recipients, { address: '', amount: '' }]);
  };

  const removeRecipient = (index: number) => {
    if (recipients.length > 1) {
      setRecipients(recipients.filter((_, i) => i !== index));
    }
  };

  const updateRecipient = (index: number, field: 'address' | 'amount', value: string) => {
    const updated = [...recipients];
    updated[index][field] = value;
    setRecipients(updated);
  };

  const handleSend = async (e: React.FormEvent) => {
    e.preventDefault();

    const validRecipients = recipients.filter(r => r.address && r.amount);
    if (validRecipients.length === 0) {
      setError('Enter at least one recipient');
      return;
    }

    for (const r of validRecipients) {
      const amt = parseFloat(r.amount);
      if (isNaN(amt) || amt <= 0) {
        setError('Invalid amount');
        return;
      }
    }

    if (totalSendMicro > totalBalanceMicro) {
      setError('Insufficient balance');
      return;
    }

    setSending(true);
    setError(null);

    try {
      const { inputs, change } = selectUtxos(utxos, totalSendMicro);

      // Resolve handles to DIDs
      const outputs: Array<{ owner: string; amount: number }> = [];
      for (const r of validRecipients) {
        let owner: string;
        if (r.address.startsWith('did:')) {
          // Already a DID, use as-is
          owner = r.address;
        } else {
          // Resolve handle to DID
          try {
            owner = await resolveHandle(r.address);
          } catch (resolveErr) {
            throw new Error(`Failed to resolve "${r.address}": ${resolveErr instanceof Error ? resolveErr.message : 'Unknown error'}`);
          }
        }
        outputs.push({
          owner,
          amount: Math.round(parseFloat(r.amount) * MICRO),
        });
      }

      if (change > 0) {
        outputs.push({ owner: session.did, amount: change });
      }

      const txInputs = inputs.map(utxo => ({
        txId: utxo.txId,
        index: utxo.index,
      }));

      const txResult = await submitTransactionWithSession(session, txInputs, outputs);
      setResult(txResult);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Transaction failed');
    } finally {
      setSending(false);
    }
  };

  if (result) {
    return (
      <Section title="SEND @TOSHIS">
        <Message type="success">SENT!</Message>
        <Stack gap="sm">
          <LabelValue label="TXID" value={result.txId} />
        </Stack>
        <ActionBar>
          <a href={`/tx/${result.txId}`} className="btn">[VIEW TX]</a>
          <button onClick={onBack}>[DONE]</button>
        </ActionBar>
      </Section>
    );
  }

  const isSingleRecipient = recipients.length === 1;

  return (
    <Section title="SEND @TOSHIS">
      <form onSubmit={handleSend}>
        <Stack gap="md">
          {recipients.map((r, i) => (
            <Stack key={i} gap="sm">
              {!isSingleRecipient && (
                <Cluster gap="sm" align="baseline">
                  <span className="text-muted">Recipient {i + 1}</span>
                  <button
                    type="button"
                    onClick={() => removeRecipient(i)}
                    className="secondary"
                    disabled={sending}
                  >
                    [REMOVE]
                  </button>
                </Cluster>
              )}
              <InputField
                label="TO"
                value={r.address}
                onChange={(v) => updateRecipient(i, 'address', v)}
                placeholder="@handle or did:plc:..."
                disabled={sending}
                width="280px"
              />
              <Cluster gap="sm" align="baseline">
                <InputField
                  label="AMOUNT"
                  value={r.amount}
                  onChange={(v) => updateRecipient(i, 'amount', v)}
                  placeholder="0"
                  type="text"
                  disabled={sending}
                  width="120px"
                />
                <span className="text-muted">@toshis</span>
                {isSingleRecipient && (
                  <button
                    type="button"
                    onClick={() => updateRecipient(0, 'amount', String(totalBalanceToshis))}
                    className="secondary"
                    disabled={sending}
                  >
                    [MAX]
                  </button>
                )}
              </Cluster>
            </Stack>
          ))}

          <button
            type="button"
            onClick={addRecipient}
            className="secondary"
            disabled={sending}
          >
            [+ ADD RECIPIENT]
          </button>

          <Stack gap="xs">
            <LabelValue
              label="SENDING"
              value={`${formatToshis(totalSendMicro)} @toshis`}
            />
            <LabelValue
              label="AVAILABLE"
              value={`${formatToshis(totalBalanceMicro)} @toshis`}
            />
          </Stack>

          {error && <Message type="error">{error}</Message>}

          <Cluster gap="sm">
            <button type="submit" disabled={sending || totalSendMicro === 0}>
              {sending ? '[SENDING...]' : '[SEND]'}
            </button>
            <button type="button" onClick={onBack} className="secondary" disabled={sending}>
              [CANCEL]
            </button>
          </Cluster>
        </Stack>
      </form>
    </Section>
  );
}

function BurnView({ session, utxos, onBack }: {
  session: OAuthSession;
  utxos: UTXO[];
  onBack: () => void;
}) {
  const [amount, setAmount] = useState(''); // in toshis
  const [burning, setBurning] = useState(false);
  const [result, setResult] = useState<TransactionResult | null>(null);
  const [error, setError] = useState<string | null>(null);

  const totalBalanceMicro = utxos.reduce((sum, u) => sum + u.amount, 0);
  const amountToshis = parseFloat(amount) || 0;
  const amountMicro = Math.round(amountToshis * MICRO);
  const remainingMicro = Math.max(0, totalBalanceMicro - amountMicro);

  const handleBurn = async (e: React.FormEvent) => {
    e.preventDefault();

    if (isNaN(amountToshis) || amountToshis <= 0) {
      setError('Invalid amount');
      return;
    }

    if (amountMicro > totalBalanceMicro) {
      setError('Insufficient balance');
      return;
    }

    setBurning(true);
    setError(null);

    try {
      const { inputs, change } = selectUtxos(utxos, amountMicro);

      const outputs: Array<{ owner: string; amount: number }> = [];
      if (change > 0) {
        outputs.push({ owner: session.did, amount: change });
      }

      if (outputs.length === 0) {
        setError('Cannot burn entire balance - keep at least 0.000001');
        setBurning(false);
        return;
      }

      const txInputs = inputs.map(utxo => ({
        txId: utxo.txId,
        index: utxo.index,
      }));

      const txResult = await submitTransactionWithSession(session, txInputs, outputs);
      setResult(txResult);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Burn failed');
    } finally {
      setBurning(false);
    }
  };

  if (result) {
    return (
      <Section title="BURN @TOSHIS">
        <Message type="success">TOKENS BURNED!</Message>
        <Stack gap="sm">
          <LabelValue label="TXID" value={result.txId} />
          <LabelValue label="BURNED" value={`${amount} @toshis`} />
        </Stack>
        <ActionBar>
          <button onClick={onBack}>[BACK]</button>
        </ActionBar>
      </Section>
    );
  }

  return (
    <Section title="BURN @TOSHIS">
      <p className="text-muted">Permanently destroy tokens. This cannot be undone.</p>
      <form onSubmit={handleBurn}>
        <Stack gap="md">
          <Cluster gap="sm" align="baseline">
            <InputField
              label="AMOUNT TO BURN"
              value={amount}
              onChange={setAmount}
              placeholder="0"
              type="text"
              disabled={burning}
              width="120px"
            />
            <span className="text-muted">@toshis</span>
          </Cluster>
          <Stack gap="xs">
            <LabelValue
              label="AVAILABLE"
              value={`${formatToshis(totalBalanceMicro)} @toshis`}
            />
            <LabelValue
              label="REMAINING AFTER BURN"
              value={`${formatToshis(remainingMicro)} @toshis`}
            />
          </Stack>
          {error && <Message type="error">ERROR: {error}</Message>}
          <Cluster gap="sm">
            <button type="submit" disabled={burning || !amount}>
              {burning ? '[BURNING...]' : '[BURN]'}
            </button>
            <button type="button" onClick={onBack} className="secondary" disabled={burning}>
              [CANCEL]
            </button>
          </Cluster>
        </Stack>
      </form>
    </Section>
  );
}

function HistoryView({ utxos, onBack }: {
  utxos: UTXO[];
  onBack: () => void;
}) {
  const txIds = [...new Set(utxos.map(u => u.txId))];

  return (
    <Section title="TRANSACTION HISTORY">
      <p className="text-muted">Recent transactions involving your UTXOs:</p>
      <DataTable
        headers={['TXID', '']}
        widths={['1fr', '4rem']}
        emptyMessage="(no transactions)"
      >
        {txIds.slice(0, 20).map(txId => (
          <DataRow
            key={txId}
            cells={[txId, '[VIEW]']}
            widths={['1fr', '4rem']}
            href={`/tx/${txId}`}
          />
        ))}
      </DataTable>
      {txIds.length > 20 && (
        <p className="text-muted">... and {txIds.length - 20} more</p>
      )}
      <ActionBar>
        <button onClick={onBack}>[BACK]</button>
      </ActionBar>
    </Section>
  );
}

function selectUtxos(utxos: UTXO[], targetAmount: number): { inputs: UTXO[]; change: number } {
  const sorted = [...utxos].sort((a, b) => b.amount - a.amount);

  const inputs: UTXO[] = [];
  let total = 0;

  for (const utxo of sorted) {
    inputs.push(utxo);
    total += utxo.amount;
    if (total >= targetAmount) break;
  }

  if (total < targetAmount) {
    throw new Error('Insufficient balance');
  }

  return {
    inputs,
    change: total - targetAmount,
  };
}
