"use client";

import Image from "next/image";
import { startTransition, useMemo, useState } from "react";
import { parsePaymentExternalReference, type PaymentsAdminData, type SerializedPaymentSession } from "@/lib/payments";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

type PaymentsQrProductOption = {
  id: string;
  name: string;
  priceMinor: number | null;
};

type PaymentsQrVendingMachineOption = {
  id: string;
  label: string;
  venueId: string;
};

type PaymentsQrOptions = {
  products: PaymentsQrProductOption[];
  vendingMachines: PaymentsQrVendingMachineOption[];
};

type GeneratedQr = {
  orderId: string;
  externalReference: string;
  amountMinor: number;
  currency: string;
  mode: string;
  description: string;
  externalPosId: string;
  vendingMachineId: string;
  productId: string;
  qrData: string | null;
};

type PaymentsAdminClientProps = {
  initialData: PaymentsAdminData;
  qrOptions: PaymentsQrOptions;
};

const emptyForm = {
  sessionId: "",
  vendingMachineId: "",
  productId: "",
  machineTxnId: "",
  externalReference: "",
  amountMinor: "0",
  currency: "ARS",
  approvedAt: "",
  lateApproval: false,
  providerPaymentId: "",
  source: "admin-manual",
};

const paymentsErrorMessages: Record<string, string> = {
  INVALID_PAYLOAD: "Hay datos inválidos en la sesión de pago.",
  PAYMENT_SESSION_NOT_FOUND: "La sesión de pago ya no existe.",
  PAYMENT_SESSION_CONFLICT: "La sesión de pago entra en conflicto con otra existente.",
  VENDING_MACHINE_NOT_FOUND: "La vending machine indicada no existe.",
  PRODUCT_NOT_FOUND: "El producto indicado no existe.",
};

function normalizeError(error: unknown) {
  if (error instanceof Error) return paymentsErrorMessages[error.message] ?? error.message;
  return "Request failed";
}

async function readError(response: Response) {
  const payload = await response.json().catch(() => null);
  return payload?.error ?? `HTTP_${response.status}`;
}

function toDateTimeLocalInput(value: string) {
  const date = new Date(value);
  const offset = date.getTimezoneOffset();
  const local = new Date(date.getTime() - offset * 60_000);
  return local.toISOString().slice(0, 16);
}

function toPayloadDate(value: string) {
  return new Date(value).toISOString();
}

const MIN_PAYMENT_AMOUNT_MINOR = 1500;

function formatMoney(amountMinor: number, currency: string) {
  return new Intl.NumberFormat("es-AR", {
    style: "currency",
    currency,
    maximumFractionDigits: 2,
  }).format(amountMinor / 100);
}

function formatMoneyInputValue(amountMinor: number | null | undefined) {
  if (amountMinor === null || amountMinor === undefined || Number.isNaN(amountMinor)) return "";
  return (amountMinor / 100).toFixed(2);
}

function parseMoneyInputToMinor(value: string) {
  const normalized = value.trim().replace(",", ".");
  if (!normalized) return null;
  if (!/^\d+(?:\.\d{1,2})?$/.test(normalized)) return null;

  const parsed = Number(normalized);
  if (!Number.isFinite(parsed)) return null;

  return Math.round(parsed * 100);
}

function formatQrPreviewUrl(qrData: string) {
  return `https://api.qrserver.com/v1/create-qr-code/?size=360x360&ecc=M&margin=2&data=${encodeURIComponent(qrData)}`;
}

function NativeSelect({
  id,
  value,
  onChange,
  disabled,
  children,
  className = "",
  describedBy,
  invalid,
}: {
  id?: string;
  value: string;
  onChange: (value: string) => void;
  disabled?: boolean;
  children: React.ReactNode;
  className?: string;
  describedBy?: string;
  invalid?: boolean;
}) {
  return (
    <select
      id={id}
      value={value}
      onChange={(event) => onChange(event.target.value)}
      disabled={disabled}
      aria-describedby={describedBy}
      aria-invalid={invalid}
      className={`h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 ${className}`.trim()}
    >
      {children}
    </select>
  );
}

export function PaymentsAdminClient({ initialData, qrOptions }: PaymentsAdminClientProps) {
  const [data, setData] = useState(initialData);
  const [busy, setBusy] = useState(false);
  const [message, setMessage] = useState<string | null>(null);
  const [messageTone, setMessageTone] = useState<"status" | "error">("status");
  const [generatedQr, setGeneratedQr] = useState<GeneratedQr | null>(null);
  const [form, setForm] = useState({
    ...emptyForm,
    approvedAt: toDateTimeLocalInput(new Date().toISOString()),
  });

  const firstVendingMachineId = qrOptions.vendingMachines[0]?.id ?? "";
  const firstProductId = qrOptions.products[0]?.id ?? "";

  const [anyAmountForm, setAnyAmountForm] = useState({
    vendingMachineId: firstVendingMachineId,
    productId: firstProductId,
    amountMinor: "15.00",
    currency: "ARS",
    mode: "dynamic" as const,
    description: "",
  });

  const [productAmountForm, setProductAmountForm] = useState({
    vendingMachineId: firstVendingMachineId,
    productId: firstProductId,
    amountMinor: "",
    currency: "ARS",
    mode: "dynamic" as const,
    description: "",
  });

  const selectedProduct = useMemo(
    () => qrOptions.products.find((item) => item.id === productAmountForm.productId) ?? qrOptions.products[0] ?? null,
    [productAmountForm.productId, qrOptions.products]
  );

  const anyAmountMinor = useMemo(() => parseMoneyInputToMinor(anyAmountForm.amountMinor), [anyAmountForm.amountMinor]);
  const anyAmountValidationError = useMemo(() => {
    if (!anyAmountForm.vendingMachineId) return "Select a vending machine.";
    if (!anyAmountForm.productId) return "Select a product.";
    if (anyAmountMinor === null) return "Use a value like 15.00.";
    if (anyAmountMinor < MIN_PAYMENT_AMOUNT_MINOR) return "Minimum amount is 15.00 ARS.";
    return null;
  }, [anyAmountForm.productId, anyAmountForm.vendingMachineId, anyAmountMinor]);

  const productAmountMinor = useMemo(() => {
    if (!productAmountForm.amountMinor.trim()) return selectedProduct?.priceMinor ?? null;
    return parseMoneyInputToMinor(productAmountForm.amountMinor);
  }, [productAmountForm.amountMinor, selectedProduct?.priceMinor]);

  const productAmountValidationError = useMemo(() => {
    if (!productAmountForm.vendingMachineId) return "Select a vending machine.";
    if (!productAmountForm.productId) return "Select a product.";
    if (!productAmountForm.amountMinor.trim() && selectedProduct?.priceMinor === null) return "This product has no default price.";
    if (productAmountForm.amountMinor.trim() && productAmountMinor === null) return "Use a value like 1000.00.";
    if (productAmountMinor !== null && productAmountMinor < MIN_PAYMENT_AMOUNT_MINOR) return "Minimum amount is 15.00 ARS.";
    return null;
  }, [productAmountForm.amountMinor, productAmountForm.productId, productAmountForm.vendingMachineId, productAmountMinor, selectedProduct?.priceMinor]);

  async function reloadData() {
    const response = await fetch("/api/admin/payments/operations", { cache: "no-store" });
    if (!response.ok) {
      throw new Error(await readError(response));
    }

    const payload = (await response.json()) as { item: PaymentsAdminData };
    startTransition(() => {
      setData(payload.item);
    });
  }

  async function runMutation(task: () => Promise<void>, successMessage: string) {
    setBusy(true);
    setMessage(null);

    try {
      await task();
      await reloadData();
      setMessageTone("status");
      setMessage(successMessage);
    } catch (error) {
      setMessageTone("error");
      setMessage(normalizeError(error));
    } finally {
      setBusy(false);
    }
  }

  function updateItem(sessionId: string, updater: (item: SerializedPaymentSession) => SerializedPaymentSession) {
    setData((current) => ({
      ...current,
      items: current.items.map((item) => {
        if (item.sessionId !== sessionId) return item;

        const next = updater(item);
        const parsedReference = parsePaymentExternalReference(next.externalReference);

        return {
          ...next,
          correlation: {
            parsedReference,
            externalReferenceIsCanonical: parsedReference !== null,
            vendingMachineIdMatchesReference: parsedReference ? parsedReference.vendingMachineId === next.vendingMachineId : false,
            machineTxnIdMatchesReference: parsedReference ? parsedReference.machineTxnId === next.machineTxnId : false,
          },
        };
      }),
    }));
  }

  async function createQrOrder(payload: Record<string, unknown>) {
    const response = await fetch("/api/integrations/payments/orders", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(payload),
    });

    if (!response.ok) {
      throw new Error(await readError(response));
    }

    const body = (await response.json()) as {
      item: {
        orderId: string;
        externalReference: string;
        amountMinor: number;
        currency: string;
        mode: string;
        externalPosId: string;
        status: string;
        statusDetail: string;
        expiresAt: string | null;
        qrData: string | null;
      };
    };

    const description = typeof payload.description === "string" ? payload.description : "";
    setGeneratedQr({
      orderId: body.item.orderId,
      externalReference: body.item.externalReference,
      amountMinor: body.item.amountMinor,
      currency: body.item.currency,
      mode: body.item.mode,
      description,
      externalPosId: body.item.externalPosId,
      vendingMachineId: String(payload.vendingMachineId ?? ""),
      productId: String(payload.productId ?? ""),
      qrData: body.item.qrData,
    });
    setMessage(`QR creado: ${body.item.orderId}`);
  }

  return (
    <div className="space-y-6">
      <div aria-live={messageTone === "error" ? "assertive" : "polite"} className="sr-only">{message ?? ""}</div>
      <section className="rounded-3xl border border-border/70 bg-card/90 p-6">
        <div className="flex flex-wrap items-start justify-between gap-4">
          <div>
            <p className="text-xs uppercase tracking-[0.24em] text-primary">Live QR generator</p>
            <h2 className="mt-2 text-2xl font-semibold">Mercado Pago QR para Ballbox</h2>
            <p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground">
              Genera QR de cobro para cualquier monto o desde un producto Ballbox con precio guardado. El resultado devuelve el order ID y el QR listo para escanear en la app de Mercado Pago.
            </p>
          </div>
          <div className="rounded-full border border-border/70 px-4 py-2 text-sm text-foreground">
            external POS: default
          </div>
        </div>

        <div className="mt-6 grid gap-6 xl:grid-cols-2">
          <article className="rounded-3xl border border-border/70 bg-background/50 p-5">
            <div className="flex items-center justify-between gap-3">
              <div>
                <p className="text-xs uppercase tracking-[0.2em] text-muted-foreground">Any amount</p>
                <h3 className="mt-1 text-lg font-semibold">Monto libre</h3>
              </div>
              <span className="rounded-full border border-border/70 px-3 py-1 text-xs text-muted-foreground">manual</span>
            </div>

            <form
              className="mt-4"
              onSubmit={(event) => {
                event.preventDefault();
                void runMutation(async () => {
                  const machine = qrOptions.vendingMachines.find((item) => item.id === anyAmountForm.vendingMachineId);
                  const product = qrOptions.products.find((item) => item.id === anyAmountForm.productId);
                  const amountMinor = anyAmountMinor;
                  if (amountMinor === null) {
                    throw new Error("INVALID_PAYLOAD");
                  }
                  if (amountMinor < MIN_PAYMENT_AMOUNT_MINOR) {
                    throw new Error("INVALID_PAYLOAD");
                  }
                  await createQrOrder({
                    vendingMachineId: anyAmountForm.vendingMachineId,
                    productId: anyAmountForm.productId,
                    amountMinor,
                    currency: anyAmountForm.currency,
                    mode: anyAmountForm.mode,
                    description:
                      anyAmountForm.description.trim() || [product?.name, machine?.label].filter(Boolean).join(" · ") || undefined,
                  });
                }, "QR any-amount created");
              }}
            >
              <fieldset className="grid gap-3 md:grid-cols-2" disabled={busy} aria-describedby="any-amount-help any-amount-error">
                <legend className="sr-only">Formulario de QR por monto libre</legend>
                <p id="any-amount-help" className="md:col-span-2 text-sm text-muted-foreground">
                  Elegí la máquina, el producto base y un monto manual en pesos argentinos.
                </p>
                <label className="text-sm text-muted-foreground" htmlFor="any-amount-vending-machine">
                  Vending machine
                  <NativeSelect id="any-amount-vending-machine" value={anyAmountForm.vendingMachineId} onChange={(value) => setAnyAmountForm((current) => ({ ...current, vendingMachineId: value }))}>
                    {qrOptions.vendingMachines.map((machine) => (
                      <option key={machine.id} value={machine.id}>
                        {machine.id} · {machine.label}
                      </option>
                    ))}
                  </NativeSelect>
                </label>
                <label className="text-sm text-muted-foreground" htmlFor="any-amount-product">
                  Product
                  <NativeSelect id="any-amount-product" value={anyAmountForm.productId} onChange={(value) => setAnyAmountForm((current) => ({ ...current, productId: value }))}>
                    {qrOptions.products.map((product) => (
                      <option key={product.id} value={product.id}>
                        {product.name}
                      </option>
                    ))}
                  </NativeSelect>
                </label>
                <label className="text-sm text-muted-foreground" htmlFor="any-amount-value">
                  Amount
                  <Input
                    id="any-amount-value"
                    value={anyAmountForm.amountMinor}
                    onChange={(event) => setAnyAmountForm((current) => ({ ...current, amountMinor: event.target.value }))}
                    placeholder="15.00"
                    inputMode="decimal"
                    disabled={busy}
                    aria-invalid={anyAmountValidationError !== null}
                    aria-describedby={anyAmountValidationError ? "any-amount-error" : undefined}
                  />
                </label>
                <label className="text-sm text-muted-foreground" htmlFor="any-amount-currency">
                  Currency
                  <NativeSelect id="any-amount-currency" value={anyAmountForm.currency} onChange={(value) => setAnyAmountForm((current) => ({ ...current, currency: value }))}>
                    <option value="ARS">ARS</option>
                  </NativeSelect>
                </label>
                <label className="text-sm text-muted-foreground md:col-span-2" htmlFor="any-amount-description">
                  Description
                  <Input
                    id="any-amount-description"
                    value={anyAmountForm.description}
                    onChange={(event) => setAnyAmountForm((current) => ({ ...current, description: event.target.value }))}
                    placeholder="Description optional"
                    disabled={busy}
                  />
                </label>
              </fieldset>

              <div className="mt-4 flex flex-wrap items-center gap-3">
                <Button type="submit" loading={busy} disabled={busy || anyAmountValidationError !== null} className={busy || anyAmountValidationError !== null ? "" : "cursor-pointer"}>
                  Generate QR
                </Button>
                {anyAmountValidationError ? <p id="any-amount-error" className="text-sm text-muted-foreground">{anyAmountValidationError}</p> : null}
              </div>
            </form>
          </article>

          <article className="rounded-3xl border border-border/70 bg-background/50 p-5">
            <div className="flex items-center justify-between gap-3">
              <div>
                <p className="text-xs uppercase tracking-[0.2em] text-muted-foreground">Per product</p>
                <h3 className="mt-1 text-lg font-semibold">Monto desde producto</h3>
              </div>
              <span className="rounded-full border border-border/70 px-3 py-1 text-xs text-muted-foreground">catalog</span>
            </div>

            <form
              className="mt-4"
              onSubmit={(event) => {
                event.preventDefault();
                void runMutation(async () => {
                  const machine = qrOptions.vendingMachines.find((item) => item.id === productAmountForm.vendingMachineId);
                  const payload: Record<string, unknown> = {
                    vendingMachineId: productAmountForm.vendingMachineId,
                    productId: productAmountForm.productId,
                    currency: productAmountForm.currency,
                    mode: productAmountForm.mode,
                    description:
                      productAmountForm.description.trim() || [selectedProduct?.name, machine?.label].filter(Boolean).join(" · ") || undefined,
                  };

                  if (productAmountForm.amountMinor.trim()) {
                    if (productAmountMinor === null) {
                      throw new Error("INVALID_PAYLOAD");
                    }
                    if (productAmountMinor < MIN_PAYMENT_AMOUNT_MINOR) {
                      throw new Error("INVALID_PAYLOAD");
                    }
                    payload.amountMinor = productAmountMinor;
                  }

                  await createQrOrder(payload);
                }, "QR product created");
              }}
            >
              <fieldset className="grid gap-3 md:grid-cols-2" disabled={busy} aria-describedby="product-amount-help product-default-price product-amount-error">
                <legend className="sr-only">Formulario de QR por producto</legend>
                <p id="product-amount-help" className="md:col-span-2 text-sm text-muted-foreground">
                  Usá el precio guardado del producto o cargá un override manual antes de generar el QR.
                </p>
                <label className="text-sm text-muted-foreground" htmlFor="product-amount-vending-machine">
                  Vending machine
                  <NativeSelect id="product-amount-vending-machine" value={productAmountForm.vendingMachineId} onChange={(value) => setProductAmountForm((current) => ({ ...current, vendingMachineId: value }))}>
                    {qrOptions.vendingMachines.map((machine) => (
                      <option key={machine.id} value={machine.id}>
                        {machine.id} · {machine.label}
                      </option>
                    ))}
                  </NativeSelect>
                </label>
                <label className="text-sm text-muted-foreground" htmlFor="product-amount-product">
                  Product
                  <NativeSelect id="product-amount-product" value={productAmountForm.productId} onChange={(value) => setProductAmountForm((current) => ({ ...current, productId: value }))}>
                    {qrOptions.products.map((product) => (
                      <option key={product.id} value={product.id}>
                        {product.name}
                      </option>
                    ))}
                  </NativeSelect>
                </label>
                <label className="text-sm text-muted-foreground" htmlFor="product-amount-value">
                  Amount override
                  <Input
                    id="product-amount-value"
                    value={productAmountForm.amountMinor}
                    onChange={(event) => setProductAmountForm((current) => ({ ...current, amountMinor: event.target.value }))}
                    placeholder={selectedProduct ? formatMoneyInputValue(selectedProduct.priceMinor) : "1000.00"}
                    inputMode="decimal"
                    disabled={busy}
                    aria-invalid={productAmountValidationError !== null}
                    aria-describedby={productAmountValidationError ? "product-amount-error" : "product-default-price"}
                  />
                </label>
                <label className="text-sm text-muted-foreground" htmlFor="product-amount-currency">
                  Currency
                  <NativeSelect id="product-amount-currency" value={productAmountForm.currency} onChange={(value) => setProductAmountForm((current) => ({ ...current, currency: value }))}>
                    <option value="ARS">ARS</option>
                  </NativeSelect>
                </label>
                <label className="text-sm text-muted-foreground md:col-span-2" htmlFor="product-amount-description">
                  Description
                  <Input
                    id="product-amount-description"
                    value={productAmountForm.description}
                    onChange={(event) => setProductAmountForm((current) => ({ ...current, description: event.target.value }))}
                    placeholder="Description optional"
                    disabled={busy}
                  />
                </label>
              </fieldset>

              <div id="product-default-price" className="mt-3 rounded-2xl border border-border/70 bg-card/70 p-3 text-sm text-muted-foreground">
                {selectedProduct ? (
                  <>
                    Default price: {selectedProduct.priceMinor !== null ? formatMoney(selectedProduct.priceMinor, "ARS") : "no price set"}. Dejá el campo vacío para usar ese valor.
                  </>
                ) : (
                  "No products configured"
                )}
              </div>

              <div className="mt-4 flex flex-wrap items-center gap-3">
                <Button type="submit" loading={busy} disabled={busy || productAmountValidationError !== null} className={busy || productAmountValidationError !== null ? "" : "cursor-pointer"}>
                  Generate QR
                </Button>
                {productAmountValidationError ? <p id="product-amount-error" className="text-sm text-muted-foreground">{productAmountValidationError}</p> : null}
              </div>
            </form>
          </article>
        </div>

        {generatedQr ? (
          <article className="mt-6 rounded-3xl border border-border/70 bg-background/50 p-5">
            <div className="flex flex-wrap items-start justify-between gap-4">
              <div>
                <p className="text-xs uppercase tracking-[0.2em] text-muted-foreground">QR result</p>
                <h3 className="mt-1 text-xl font-semibold">{generatedQr.orderId}</h3>
                <p className="mt-2 text-sm text-muted-foreground">
                  {formatMoney(generatedQr.amountMinor, generatedQr.currency)} · {generatedQr.mode} · {generatedQr.externalPosId}
                </p>
                <p className="mt-2 text-sm text-muted-foreground">{generatedQr.description || generatedQr.externalReference}</p>
              </div>
              <div className="rounded-full border border-border/70 px-4 py-2 text-sm text-foreground">{generatedQr.vendingMachineId}</div>
            </div>

            <div className="mt-6 grid gap-6 lg:grid-cols-[auto_1fr] lg:items-start">
              <div className="rounded-3xl border border-border/70 bg-card/80 p-4">
                {generatedQr.qrData ? (
                  <Image
                    src={formatQrPreviewUrl(generatedQr.qrData)}
                    alt={`QR for ${generatedQr.orderId}`}
                    width={320}
                    height={320}
                    unoptimized
                    className="h-[320px] w-[320px] rounded-2xl border border-border/60 bg-white p-3"
                  />
                ) : (
                  <div className="flex h-[320px] w-[320px] items-center justify-center rounded-2xl border border-border/60 bg-background/70 text-sm text-muted-foreground">
                    No QR data returned
                  </div>
                )}
              </div>

              <div className="space-y-4 text-sm">
                <div className="rounded-2xl border border-border/70 bg-card/80 p-4">
                  <p className="text-xs uppercase tracking-[0.18em] text-muted-foreground">External reference</p>
                  <p className="mt-2 font-mono text-foreground break-all">{generatedQr.externalReference}</p>
                </div>
                <div className="rounded-2xl border border-border/70 bg-card/80 p-4">
                  <p className="text-xs uppercase tracking-[0.18em] text-muted-foreground">QR payload</p>
                  <p className="mt-2 font-mono text-xs leading-6 text-foreground break-all">{generatedQr.qrData ?? "null"}</p>
                </div>
                <div className="rounded-2xl border border-border/70 bg-card/80 p-4">
                  <p className="text-xs uppercase tracking-[0.18em] text-muted-foreground">Flow</p>
                  <p className="mt-2 text-foreground">Create QR in Ballbox, scan with a real Mercado Pago buyer account, and verify the webhook writes a session to `/admin/payments`.</p>
                </div>
              </div>
            </div>
          </article>
        ) : null}
      </section>

      <section className="grid gap-4 md:grid-cols-5">
        <div className="rounded-3xl border border-border/70 bg-card/90 p-6">
          <p className="text-sm text-muted-foreground">Sessions</p>
          <p className="mt-2 text-4xl font-semibold">{data.summary.sessions}</p>
        </div>
        <div className="rounded-3xl border border-border/70 bg-card/90 p-6">
          <p className="text-sm text-muted-foreground">Vending machines</p>
          <p className="mt-2 text-4xl font-semibold">{data.summary.vendingMachines}</p>
        </div>
        <div className="rounded-3xl border border-border/70 bg-card/90 p-6">
          <p className="text-sm text-muted-foreground">Late approvals</p>
          <p className="mt-2 text-4xl font-semibold">{data.summary.lateApprovals}</p>
        </div>
        <div className="rounded-3xl border border-border/70 bg-card/90 p-6">
          <p className="text-sm text-muted-foreground">Reference mismatches</p>
          <p className="mt-2 text-4xl font-semibold">{data.summary.correlationMismatches}</p>
        </div>
        <div className="rounded-3xl border border-border/70 bg-card/90 p-6">
          <p className="text-sm text-muted-foreground">Approved totals</p>
          <div className="mt-2 space-y-1">
            {data.summary.amountTotals.map((item) => (
              <p key={item.currency} className="text-lg font-semibold">
                {formatMoney(item.amountMinor, item.currency)}
              </p>
            ))}
          </div>
        </div>
      </section>

      <section className="rounded-3xl border border-border/70 bg-card/90 p-6">
        <div className="flex flex-wrap items-start justify-between gap-4">
          <div>
            <p className="text-xs uppercase tracking-[0.24em] text-primary">Payments ops</p>
            <h2 className="mt-2 text-2xl font-semibold">Approved payment sessions</h2>
            <p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground">
              Surface admin para reconciliar callbacks aprobados persistidos localmente. Corrige referencia, vending machine y metadata sobre el flujo de pagos ya centralizado en Ballbox.
            </p>
          </div>
          <div className="rounded-full border border-border/70 px-4 py-2 text-sm text-foreground">{data.source}</div>
        </div>

        <form
          className="mt-6"
          onSubmit={(event) => {
            event.preventDefault();
            void runMutation(async () => {
              const response = await fetch("/api/admin/payments/sessions", {
                method: "POST",
                headers: { "content-type": "application/json" },
                body: JSON.stringify({
                  ...form,
                  amountMinor: Number(form.amountMinor),
                  approvedAt: toPayloadDate(form.approvedAt),
                  providerPaymentId: form.providerPaymentId || undefined,
                }),
              });

              if (!response.ok) {
                throw new Error(await readError(response));
              }

              setForm({
                ...emptyForm,
                approvedAt: toDateTimeLocalInput(new Date().toISOString()),
              });
            }, "Payment session created");
          }}
        >
          <fieldset className="space-y-4" disabled={busy} aria-describedby="payments-create-help payments-create-status">
            <legend className="sr-only">Create approved payment session</legend>
            <p id="payments-create-help" className="text-sm text-muted-foreground">
              Cargá una sesión aprobada manual para reconciliar webhooks o referencias fuera del flujo normal.
            </p>
            <div className="grid gap-3 lg:grid-cols-[1fr_1fr_1fr_1fr_1fr]">
              <label className="text-sm text-muted-foreground" htmlFor="create-session-id">
                Session id
                <Input id="create-session-id" value={form.sessionId} onChange={(event) => setForm((current) => ({ ...current, sessionId: event.target.value }))} placeholder="session_id" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-vending-machine-id">
                Vending machine id
                <Input id="create-vending-machine-id" value={form.vendingMachineId} onChange={(event) => setForm((current) => ({ ...current, vendingMachineId: event.target.value }))} placeholder="vending_machine_id" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-product-id">
                Product id
                <Input id="create-product-id" value={form.productId} onChange={(event) => setForm((current) => ({ ...current, productId: event.target.value }))} placeholder="product_id" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-machine-txn-id">
                Machine transaction id
                <Input id="create-machine-txn-id" value={form.machineTxnId} onChange={(event) => setForm((current) => ({ ...current, machineTxnId: event.target.value }))} placeholder="machine_txn_id" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-external-reference">
                External reference
                <Input id="create-external-reference" value={form.externalReference} onChange={(event) => setForm((current) => ({ ...current, externalReference: event.target.value }))} placeholder="payment:vendingMachine:txn" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-amount-minor">
                Amount minor
                <Input id="create-amount-minor" value={form.amountMinor} onChange={(event) => setForm((current) => ({ ...current, amountMinor: event.target.value }))} placeholder="amount_minor" inputMode="numeric" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-currency">
                Currency
                <Input id="create-currency" value={form.currency} onChange={(event) => setForm((current) => ({ ...current, currency: event.target.value }))} placeholder="ARS" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-approved-at">
                Approved at
                <Input id="create-approved-at" value={form.approvedAt} onChange={(event) => setForm((current) => ({ ...current, approvedAt: event.target.value }))} type="datetime-local" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-provider-payment-id">
                Provider payment id
                <Input id="create-provider-payment-id" value={form.providerPaymentId} onChange={(event) => setForm((current) => ({ ...current, providerPaymentId: event.target.value }))} placeholder="provider_payment_id" />
              </label>
              <label className="text-sm text-muted-foreground" htmlFor="create-source">
                Source
                <Input id="create-source" value={form.source} onChange={(event) => setForm((current) => ({ ...current, source: event.target.value }))} placeholder="source" />
              </label>
            </div>

            <label className="flex items-center gap-3 text-sm text-muted-foreground" htmlFor="create-late-approval">
              <input id="create-late-approval" type="checkbox" checked={form.lateApproval} onChange={(event) => setForm((current) => ({ ...current, lateApproval: event.target.checked }))} />
              late approval
            </label>

            <div className="flex flex-wrap items-center gap-3">
              <Button
                type="submit"
                loading={busy}
                disabled={
                  busy ||
                  !form.sessionId.trim() ||
                  !form.vendingMachineId.trim() ||
                  !form.productId.trim() ||
                  !form.machineTxnId.trim() ||
                  !form.externalReference.trim() ||
                  !form.approvedAt.trim()
                }
              >
                Add session
              </Button>
              {message ? <p id="payments-create-status" role={messageTone === "error" ? "alert" : "status"} className="text-sm text-muted-foreground">{message}</p> : <p id="payments-create-status" className="sr-only" />}
            </div>
          </fieldset>
        </form>
      </section>

      <section className="space-y-4" aria-label="Approved payment session list">
        {data.items.map((item) => {
          const hasMismatch =
            !item.correlation.vendingMachineIdMatchesReference || !item.correlation.machineTxnIdMatchesReference;

          return (
            <article key={item.sessionId} className="rounded-3xl border border-border/70 bg-card/90 p-6" aria-labelledby={`payment-session-${item.sessionId}`}>
              <div className="flex flex-wrap items-start justify-between gap-4">
                <div>
                  <p className="text-xs uppercase tracking-[0.24em] text-primary">Session</p>
                  <h3 id={`payment-session-${item.sessionId}`} className="mt-2 text-lg font-semibold">{item.sessionId}</h3>
                  <p className="mt-2 text-sm text-muted-foreground">
                    {formatMoney(item.amountMinor, item.currency)} · {item.currency} · {item.status}
                  </p>
                </div>
                <div className="flex flex-wrap gap-2 text-xs">
                  <span className="rounded-full border border-border/70 px-3 py-1 text-muted-foreground">{item.source}</span>
                  {item.lateApproval ? <span className="rounded-full border border-amber-500/40 px-3 py-1 text-amber-700">late</span> : null}
                  {hasMismatch ? <span className="rounded-full border border-red-500/40 px-3 py-1 text-red-700">reference mismatch</span> : null}
                </div>
              </div>

              <fieldset className="mt-5 space-y-3" disabled={busy} aria-describedby={`payment-session-${item.sessionId}-summary`}>
                <legend className="sr-only">Edit payment session {item.sessionId}</legend>
                <div className="grid gap-3 lg:grid-cols-[1fr_1fr_1fr_1fr_1fr]">
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-vending-machine`}>
                    Vending machine id
                    <Input
                      id={`payment-session-${item.sessionId}-vending-machine`}
                      value={item.vendingMachineId}
                      onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, vendingMachineId: event.target.value }))}
                      disabled={busy}
                    />
                  </label>
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-product`}>
                    Product id
                    <Input id={`payment-session-${item.sessionId}-product`} value={item.productId} onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, productId: event.target.value }))} disabled={busy} />
                  </label>
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-machine-txn`}>
                    Machine transaction id
                    <Input id={`payment-session-${item.sessionId}-machine-txn`} value={item.machineTxnId} onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, machineTxnId: event.target.value }))} disabled={busy} />
                  </label>
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-external-reference`}>
                    External reference
                    <Input
                      id={`payment-session-${item.sessionId}-external-reference`}
                      value={item.externalReference}
                      onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, externalReference: event.target.value }))}
                      disabled={busy}
                    />
                  </label>
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-amount`}>
                    Amount minor
                    <Input
                      id={`payment-session-${item.sessionId}-amount`}
                      value={String(item.amountMinor)}
                      onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, amountMinor: Number(event.target.value || "0") }))}
                      disabled={busy}
                      inputMode="numeric"
                    />
                  </label>
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-currency`}>
                    Currency
                    <Input id={`payment-session-${item.sessionId}-currency`} value={item.currency} onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, currency: event.target.value }))} disabled={busy} />
                  </label>
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-approved-at`}>
                    Approved at
                    <Input
                      id={`payment-session-${item.sessionId}-approved-at`}
                      value={toDateTimeLocalInput(item.approvedAt)}
                      onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, approvedAt: toPayloadDate(event.target.value) }))}
                      type="datetime-local"
                      disabled={busy}
                    />
                  </label>
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-provider-payment-id`}>
                    Provider payment id
                    <Input
                      id={`payment-session-${item.sessionId}-provider-payment-id`}
                      value={item.providerPaymentId ?? ""}
                      onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, providerPaymentId: event.target.value || null }))}
                      disabled={busy}
                    />
                  </label>
                  <label className="text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-source`}>
                    Source
                    <Input id={`payment-session-${item.sessionId}-source`} value={item.source} onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, source: event.target.value }))} disabled={busy} />
                  </label>
                  <label className="flex h-full min-h-10 items-center gap-3 rounded-md border border-input px-3 text-sm text-muted-foreground" htmlFor={`payment-session-${item.sessionId}-late-approval`}>
                    <input
                      id={`payment-session-${item.sessionId}-late-approval`}
                      type="checkbox"
                      checked={item.lateApproval}
                      onChange={(event) => updateItem(item.sessionId, (current) => ({ ...current, lateApproval: event.target.checked }))}
                      disabled={busy}
                    />
                    late approval
                  </label>
                </div>
              </fieldset>

              <div id={`payment-session-${item.sessionId}-summary`} className="mt-4 rounded-2xl border border-border/70 bg-background/40 p-4 text-sm text-muted-foreground">
                <p>
                  Parsed reference: {" "}
                  {item.correlation.parsedReference
                    ? `${item.correlation.parsedReference.vendingMachineId} / ${item.correlation.parsedReference.machineTxnId}`
                    : "invalid"}
                </p>
                <p className="mt-2">Provider payment id: {item.providerPaymentId ?? "none"}</p>
              </div>

              <div className="mt-4 flex flex-wrap gap-3">
                <Button
                  loading={busy}
                  variant="outline"
                  disabled={busy}
                  onClick={() =>
                    runMutation(async () => {
                      const response = await fetch(`/api/admin/payments/sessions/${item.sessionId}`, {
                        method: "PATCH",
                        headers: { "content-type": "application/json" },
                        body: JSON.stringify({
                          vendingMachineId: item.vendingMachineId,
                          productId: item.productId,
                          machineTxnId: item.machineTxnId,
                          externalReference: item.externalReference,
                          amountMinor: item.amountMinor,
                          currency: item.currency,
                          approvedAt: item.approvedAt,
                          lateApproval: item.lateApproval,
                          providerPaymentId: item.providerPaymentId ?? "",
                          source: item.source,
                        }),
                      });

                      if (!response.ok) {
                        throw new Error(await readError(response));
                      }
                    }, "Payment session updated")
                  }
                >
                  Save
                </Button>
                <Button
                  loading={busy}
                  variant="destructive"
                  disabled={busy}
                  onClick={() =>
                    runMutation(async () => {
                      const response = await fetch(`/api/admin/payments/sessions/${item.sessionId}`, { method: "DELETE" });
                      if (!response.ok) {
                        throw new Error(await readError(response));
                      }
                    }, "Payment session deleted")
                  }
                >
                  Delete
                </Button>
              </div>
            </article>
          );
        })}
      </section>
    </div>
  );
}
