import type { DecodedInvoice } from '../shared';
import { useState } from 'react';
import { Loader2, Zap } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { formatTimestamp, satsToMsat, truncateMiddle } from '../format';
import { Amount } from '../Amount';
import { useWalletOperations } from '../useWalletData';

// Pay a BOLT11 invoice: decode first, confirm what you are about to pay, then pay.
//
// The two steps are deliberate. A BOLT11 string is opaque, so paying one straight from the paste box is
// signing something you cannot read. Decoding is a free read — it does not need the wallet unlocked — and
// it is the only chance to see the amount and the destination before the money moves.
//
// Amounts stay msat STRINGS end to end (see format.ts): the decoded amount is rendered as a string and a
// zero-amount invoice's manual amount is converted with satsToMsat rather than a multiplication.

type PayInvoiceDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; walletId: number };

export const PayInvoiceDialog = ({ open, onOpenChange, walletId }: PayInvoiceDialogProps) => {
  const { decode, pay } = useWalletOperations(walletId);
  const [bolt11, setBolt11] = useState('');
  const [decoded, setDecoded] = useState<DecodedInvoice | null>(null);
  const [amountSats, setAmountSats] = useState('');
  const [feeLimitSats, setFeeLimitSats] = useState('');

  const close = () => {
    setBolt11('');
    setDecoded(null);
    setAmountSats('');
    setFeeLimitSats('');
    onOpenChange(false);
  };

  const runDecode = async (ev: React.FormEvent) => {
    ev.preventDefault();
    const trimmed = bolt11.trim();
    if (!trimmed) return;
    const result = await decode.mutateAsync(trimmed);
    setDecoded(result.decoded);
  };

  // A zero-amount invoice carries no amountMsat, so the payer names the figure.
  const needsAmount = decoded != null && decoded.amountMsat == null;
  const manualSats = Number(amountSats);
  const feeLimit = Number(feeLimitSats);
  const canPay = decoded != null && (!needsAmount || (Number.isFinite(manualSats) && manualSats > 0)) && !pay.isPending;

  const runPay = async () => {
    if (!decoded || !canPay) return;
    await pay.mutateAsync({
      bolt11: decoded.bolt11,
      amountMsat: needsAmount ? satsToMsat(manualSats) : undefined,
      feeLimitMsat: Number.isFinite(feeLimit) && feeLimit > 0 ? satsToMsat(feeLimit) : undefined,
    });
    close();
  };

  return (
    <Dialog open={open} onOpenChange={(next) => (next ? onOpenChange(true) : close())}>
      <DialogContent className="max-w-md">
        <DialogHeader>
          <DialogTitle>Pay an invoice</DialogTitle>
          <DialogDescription>
            {decoded
              ? 'Check this before you pay — a lightning payment cannot be reversed.'
              : 'Paste a BOLT11 invoice.'}
          </DialogDescription>
        </DialogHeader>

        {decoded ? (
          <div className="mt-4 space-y-4">
            <dl className="space-y-2 rounded-lg border border-border bg-muted/40 p-3 text-xs">
              <Row label="Amount">
                {decoded.amountMsat == null ? (
                  <span className="text-muted-foreground">payer chooses</span>
                ) : (
                  <Amount msat={decoded.amountMsat} className="font-semibold" />
                )}
              </Row>
              <Row label="Description">{decoded.description || <span className="text-muted-foreground">—</span>}</Row>
              <Row label="Destination">
                <code className="font-mono text-[11px]">{truncateMiddle(decoded.destination, 12, 8)}</code>
              </Row>
              <Row label="Expires">{formatTimestamp(decoded.timestamp + decoded.expiry)}</Row>
            </dl>

            {needsAmount && (
              <div className="space-y-1.5">
                <Label htmlFor="wallet-pay-amount">Amount to pay (sats)</Label>
                <Input
                  id="wallet-pay-amount"
                  type="number"
                  min={1}
                  value={amountSats}
                  onChange={(ev) => setAmountSats(ev.target.value)}
                  className="w-40"
                />
              </div>
            )}

            <div className="space-y-1.5">
              <Label htmlFor="wallet-pay-fee">Max routing fee (sats, optional)</Label>
              <Input
                id="wallet-pay-fee"
                type="number"
                min={0}
                value={feeLimitSats}
                placeholder="node default"
                onChange={(ev) => setFeeLimitSats(ev.target.value)}
                className="w-40"
              />
            </div>

            <DialogFooter>
              <Button type="button" variant="ghost" onClick={() => setDecoded(null)}>
                Back
              </Button>
              <Button type="button" onClick={runPay} disabled={!canPay}>
                {pay.isPending ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Zap className="mr-2 h-4 w-4" />}
                Pay
              </Button>
            </DialogFooter>
          </div>
        ) : (
          <form onSubmit={runDecode} className="mt-4 space-y-4">
            <div className="space-y-1.5">
              <Label htmlFor="wallet-pay-bolt11">Invoice</Label>
              <Input
                id="wallet-pay-bolt11"
                value={bolt11}
                spellCheck={false}
                placeholder="lnbc…"
                onChange={(ev) => setBolt11(ev.target.value)}
                className="font-mono text-xs"
              />
            </div>

            <DialogFooter>
              <Button type="button" variant="ghost" onClick={close}>
                Cancel
              </Button>
              <Button type="submit" disabled={!bolt11.trim() || decode.isPending}>
                {decode.isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
                Decode
              </Button>
            </DialogFooter>
          </form>
        )}
      </DialogContent>
    </Dialog>
  );
};

const Row = ({ label, children }: { label: string; children: React.ReactNode }) => (
  <div className="flex items-baseline justify-between gap-3">
    <dt className="shrink-0 text-muted-foreground">{label}</dt>
    <dd className="min-w-0 truncate text-right">{children}</dd>
  </div>
);
