import { Box } from "@/components/box";
import { ConfirmDialog } from "@/components/confirm-dialog";
import { Heading } from "@/components/heading";
import { TimeInput } from "@/components/time-input";
import { Button } from "@/components/ui/button";
import { FloatingDatePicker } from "@/components/ui/FloatingDatePicker";
import { Input } from "@/components/ui/input";
import {
  InputGroup,
  InputGroupInput,
  InputGroupText,
} from "@/components/ui/input-group";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { useEventEditContext } from "@/hooks/EventEditContext";
import { useSettings } from "@/hooks/SettingsContext";
import { ensureUtcZ, getDateInTimezone } from "@/lib/date-utils";
import { callLocalApi } from "@/lib/remote";
import { showToast, showToastError } from "@/lib/toast";
import { cn, getCurrencySymbol } from "@/lib/utils";
import { __ } from "@wordpress/i18n";
import {
  ChevronDown,
  ChevronUp,
  GripVertical,
  MoveRight,
  Plus,
  Trash2,
} from "lucide-react";
import { DateTime } from "luxon";
import { isValid } from "date-fns";
import { useEffect, useMemo, useRef, useState } from "react";
import { Link } from "react-router-dom";

const DEFAULT_CURRENCY = "USD";

// Split visibility toggles fall back to the legacy catch-all value for
// events saved before "Show unavailable tickets" became three switches.
function readShowFlag(event, key) {
  const value = event?.[key];
  if (value === undefined || value === null || value === "") {
    const legacy = event?.tickets_show_unavailable;
    return legacy === undefined || legacy === null || legacy === ""
      ? false
      : !!legacy;
  }
  return !!value;
}

function SettingToggle({ id, label, description, checked, onCheckedChange }) {
  return (
    <div className="flex items-start gap-4 max-w-[500px]">
      <Switch
        id={id}
        checked={checked}
        onCheckedChange={onCheckedChange}
        className="mt-[1px]"
      />
      <div className="space-y-1">
        <Label className="font-medium" htmlFor={id}>
          {label}
        </Label>
        {description && (
          <p className="text-sm text-muted-foreground">{description}</p>
        )}
      </div>
    </div>
  );
}

export function EventEditManageTickets() {
  const { event, setEvent, registerBeforeSave } = useEventEditContext();
  const { settings: globalSettings } = useSettings();
  const [tickets, setTickets] = useState([]);
  const [loadingTickets, setLoadingTickets] = useState(false);
  const [savingTickets, setSavingTickets] = useState(false);
  const [expandedTickets, setExpandedTickets] = useState({});
  const [hasLoadedTickets, setHasLoadedTickets] = useState(false);
  const dragIndexRef = useRef(null);
  const [dragOverIndex, setDragOverIndex] = useState(null);
  const [draggingIndex, setDraggingIndex] = useState(null);
  const ticketsRef = useRef([]);
  // Saved ticket IDs the user has marked for deletion since last save.
  // The DELETE REST call is deferred until saveTickets() — refreshing
  // before saving keeps the ticket intact.
  const [deletedTicketIds, setDeletedTicketIds] = useState([]);
  const deletedTicketIdsRef = useRef([]);
  const [deleteConfirmTarget, setDeleteConfirmTarget] = useState(null);
  const expandedStorageKey = event?.id
    ? `eventkoi_tickets_expanded_${event.id}`
    : null;
  const [ticketSettings, setTicketSettings] = useState({
    auto_create_account: false,
    show_remaining: true,
    show_unavailable: false,
    show_sold_out: false,
    show_upcoming: false,
    show_ended: false,
    event_capacity: "",
    terms_conditions: "",
    terms_conditions_required: false,
    agreements: [],
  });

  const wpTz = useMemo(
    () =>
      event?.timezone || window.eventkoi_params?.timezone_string || "UTC",
    [event?.timezone]
  );
  const globalCurrency = String(
    globalSettings?.currency || DEFAULT_CURRENCY
  ).toUpperCase();
  const currencySymbol = getCurrencySymbol(globalCurrency);

  useEffect(() => {
    if (event) {
      const showRemainingRaw = event.tickets_show_remaining;
      const showRemaining =
        showRemainingRaw === undefined ||
        showRemainingRaw === null ||
        showRemainingRaw === ""
          ? true
          : !!showRemainingRaw;

      setTicketSettings({
        auto_create_account: event.tickets_auto_create_account || false,
        show_remaining: showRemaining,
        show_unavailable:
          event.tickets_show_unavailable === undefined ||
          event.tickets_show_unavailable === null ||
          event.tickets_show_unavailable === ""
            ? false
            : !!event.tickets_show_unavailable,
        show_sold_out: readShowFlag(event, "tickets_show_sold_out"),
        show_upcoming: readShowFlag(event, "tickets_show_upcoming"),
        show_ended: readShowFlag(event, "tickets_show_ended"),
        event_capacity:
          event.tickets_event_capacity > 0
            ? String(event.tickets_event_capacity)
            : "",
        terms_conditions: event.tickets_terms_conditions || "",
        terms_conditions_required: !!event.tickets_terms_conditions_required,
        agreements: Array.isArray(event.tickets_agreements)
          ? event.tickets_agreements
          : [],
      });
    }
  }, [event]);

  useEffect(() => {
    ticketsRef.current = tickets;
  }, [tickets]);

  useEffect(() => {
    deletedTicketIdsRef.current = deletedTicketIds;
  }, [deletedTicketIds]);

  useEffect(() => {
    const fetchTickets = async () => {
      if (!event?.id) {
        setTickets([]);
        setDeletedTicketIds([]);
        return;
      }

      setLoadingTickets(true);
      try {
        const response = await callLocalApi(`events/${event.id}/tickets`, {
          method: "GET",
        });

        const ticketsList = Array.isArray(response?.tickets)
          ? response.tickets.map(normalizeTicket)
          : [];

        setTickets(ticketsList);
        // Server is the source of truth after a fresh fetch — clear any
        // stale pending deletions from a previous event/edit session.
        setDeletedTicketIds([]);

        const storedExpanded =
          expandedStorageKey &&
          typeof window !== "undefined" &&
          window.localStorage
            ? window.localStorage.getItem(expandedStorageKey)
            : null;
        let parsedExpanded = null;
        if (storedExpanded) {
          try {
            parsedExpanded = JSON.parse(storedExpanded);
          } catch (e) {
            parsedExpanded = null;
          }
        }

        setExpandedTickets(
          ticketsList.reduce((acc, ticket) => {
            const storedValue =
              parsedExpanded &&
              Object.prototype.hasOwnProperty.call(
                parsedExpanded,
                String(ticket.id)
              )
                ? parsedExpanded[String(ticket.id)]
                : null;
            acc[ticket.id] = typeof storedValue === "boolean" ? storedValue : true;
            return acc;
          }, {})
        );
      } catch (error) {
        showToastError(__("Failed to load tickets.", "eventkoi-lite"));
      } finally {
        setLoadingTickets(false);
        setHasLoadedTickets(true);
      }
    };

    fetchTickets();
  }, [event?.id]);

  useEffect(() => {
    if (loadingTickets || !event?.id) return;
    if (tickets.length > 0) return;
    const initialTicket = buildNewTicket();
    setTickets([initialTicket]);
    setExpandedTickets({ [initialTicket.tempId]: true });
  }, [loadingTickets, tickets.length, event?.id]);

  useEffect(() => {
    if (!expandedStorageKey || !event?.id || !hasLoadedTickets) return;
    if (typeof window === "undefined" || !window.localStorage) return;
    const stored = Object.entries(expandedTickets).reduce((acc, [key, value]) => {
      if (String(key).startsWith("temp-")) return acc;
      acc[String(key)] = value;
      return acc;
    }, {});
    try {
      window.localStorage.setItem(expandedStorageKey, JSON.stringify(stored));
    } catch (e) {
      // Safari private mode can throw — preference loss is acceptable.
    }
  }, [expandedTickets, expandedStorageKey, event?.id, hasLoadedTickets]);

  const handleToggle = (key) => {
    const newSettings = {
      ...ticketSettings,
      [key]: !ticketSettings[key],
    };
    setTicketSettings(newSettings);
    updateEvent(newSettings);
  };

  const handleTextChange = (e) => {
    const newSettings = {
      ...ticketSettings,
      terms_conditions: e.target.value,
    };
    setTicketSettings(newSettings);
    updateEvent(newSettings);
  };

  const updateAgreements = (nextAgreements) => {
    const newSettings = {
      ...ticketSettings,
      agreements: nextAgreements,
    };
    setTicketSettings(newSettings);
    updateEvent(newSettings);
  };

  const handleAgreementChange = (index, patch) => {
    updateAgreements(
      (ticketSettings.agreements || []).map((item, idx) =>
        idx === index ? { ...item, ...patch } : item,
      ),
    );
  };

  const handleAddAgreement = () => {
    updateAgreements([
      ...(ticketSettings.agreements || []),
      {
        id: `agr_${crypto?.randomUUID?.() || `${Date.now()}${Math.floor(Math.random() * 1e9)}`}`,
        text: "",
        required: true,
      },
    ]);
  };

  const handleRemoveAgreement = (index) => {
    updateAgreements(
      (ticketSettings.agreements || []).filter((_, idx) => idx !== index),
    );
  };

  const handleCapacityChange = (e) => {
    const digits = String(e.target.value || "").replace(/[^0-9]/g, "");
    const newSettings = {
      ...ticketSettings,
      event_capacity: digits,
    };
    setTicketSettings(newSettings);
    updateEvent(newSettings);
  };

  const updateEvent = (newSettings) => {
    if (!event?.id) return;

    const updatedEvent = {
      ...event,
      tickets_require_account: false,
      tickets_auto_create_account: newSettings.auto_create_account,
      tickets_show_remaining: newSettings.show_remaining,
      tickets_show_unavailable:
        !!newSettings.show_sold_out ||
        !!newSettings.show_upcoming ||
        !!newSettings.show_ended,
      tickets_show_sold_out: !!newSettings.show_sold_out,
      tickets_show_upcoming: !!newSettings.show_upcoming,
      tickets_show_ended: !!newSettings.show_ended,
      tickets_terms_conditions: newSettings.terms_conditions,
      tickets_terms_conditions_required: newSettings.terms_conditions_required,
      tickets_agreements: Array.isArray(newSettings.agreements)
        ? newSettings.agreements
        : [],
      tickets_event_capacity:
        newSettings.event_capacity === ""
          ? 0
          : Math.max(0, parseInt(newSettings.event_capacity, 10) || 0),
      tickets_display_mode: "cards",
    };

    setEvent?.(updatedEvent);
  };

  const formatPriceDisplay = (value) => {
    if (value === null || value === undefined || value === "") return "";
    const asString = String(value);
    if (!asString.includes(".")) return asString;
    return asString.replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
  };

  const normalizeTicket = (ticket) => ({
    ...ticket,
    currency: globalCurrency,
    price: formatPriceDisplay(ticket?.price),
    max_per_order:
      ticket?.max_per_order === null || ticket?.max_per_order === undefined
        ? ""
        : String(ticket.max_per_order),
  });

  const buildNewTicket = () => ({
    tempId: `temp-${crypto?.randomUUID?.() || Date.now()}`,
    name: "",
    description: "",
    price: "",
    currency: globalCurrency,
    quantity_available: "",
    max_per_order: "",
    sale_start: null,
    sale_end: null,
    terms_conditions: "",
    status: "active",
  });

  const handleAddTicket = () => {
    const newTicket = buildNewTicket();
    setTickets((prev) => [...prev, newTicket]);
    setExpandedTickets((prev) => ({
      ...prev,
      [newTicket.tempId]: true,
    }));
  };

  const handleTicketChange = (index, updates) => {
    setTickets((prev) => {
      const next = [...prev];
      next[index] = { ...next[index], ...updates };
      return next;
    });
  };

  const toggleTicket = (key) => {
    setExpandedTickets((prev) => {
      const next = {
        ...prev,
        [key]: !prev[key],
      };

      if (
        expandedStorageKey &&
        event?.id &&
        typeof window !== "undefined" &&
        window.localStorage &&
        !String(key).startsWith("temp-")
      ) {
        const stored = Object.entries(next).reduce((acc, [k, value]) => {
          if (String(k).startsWith("temp-")) return acc;
          acc[String(k)] = value;
          return acc;
        }, {});
        window.localStorage.setItem(expandedStorageKey, JSON.stringify(stored));
      }

      return next;
    });
  };

  const handleDragStart = (index) => (e) => {
    dragIndexRef.current = index;
    setDraggingIndex(index);
    e.dataTransfer.effectAllowed = "move";
  };

  const handleDragEnd = () => {
    dragIndexRef.current = null;
    setDraggingIndex(null);
    setDragOverIndex(null);
  };

  const handleDragOver = (index) => (e) => {
    e.preventDefault();
    if (draggingIndex === null) return;
    if (dragOverIndex !== index) setDragOverIndex(index);
  };

  const handleDrop = (index) => (e) => {
    e.preventDefault();
    if (draggingIndex === null || draggingIndex === index) return;
    setTickets((prev) => {
      const next = [...prev];
      const [moved] = next.splice(draggingIndex, 1);
      next.splice(index, 0, moved);
      return next;
    });
    dragIndexRef.current = index;
    setDraggingIndex(null);
    setDragOverIndex(null);
  };

  // Removes the row from local state. For saved tickets we also stash the
  // server ID into deletedTicketIds so saveTickets() commits the deletion;
  // unsaved rows just disappear (there's nothing on the server to delete).
  const removeTicketRow = (ticket, index) => {
    if (ticket?.id) {
      const id = ticket.id;
      setDeletedTicketIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
    }
    setTickets((prev) => prev.filter((_, idx) => idx !== index));
    setDeleteConfirmTarget(null);
  };

  const handleDeleteTicket = (ticket, index) => {
    // Unsaved rows: drop immediately, no confirmation (nothing to lose).
    if (!ticket?.id) {
      removeTicketRow(ticket, index);
      return;
    }
    // Saved rows: confirm first; commit the server deletion on Save.
    setDeleteConfirmTarget({ ticket, index });
  };

  const ticketHasData = (ticket) => {
    if (!ticket) return false;
    const hasName = !!ticket.name && ticket.name.trim().length > 0;
    const hasDescription =
      !!ticket.description && ticket.description.trim().length > 0;
    const hasPrice =
      ticket.price !== "" &&
      ticket.price !== null &&
      typeof ticket.price !== "undefined";
    const hasQuantity =
      ticket.quantity_available !== "" &&
      ticket.quantity_available !== null &&
      typeof ticket.quantity_available !== "undefined";
    const hasMaxPerOrder =
      ticket.max_per_order !== "" &&
      ticket.max_per_order !== null &&
      typeof ticket.max_per_order !== "undefined";
    const hasSaleStart = !!ticket.sale_start;
    const hasSaleEnd = !!ticket.sale_end;
    const hasTerms =
      !!ticket.terms_conditions && ticket.terms_conditions.trim().length > 0;

    return (
      hasName ||
      hasDescription ||
      hasPrice ||
      hasQuantity ||
      hasMaxPerOrder ||
      hasSaleStart ||
      hasSaleEnd ||
      hasTerms
    );
  };

  const isEmptyNewTicket = (ticket) => !ticket?.id && !ticketHasData(ticket);

  const validateTickets = (ticketsToValidate) => {
    const missingName = ticketsToValidate.find((ticket) => {
      if (isEmptyNewTicket(ticket)) {
        return false;
      }
      return !ticket?.name || !ticket.name.trim();
    });

    if (missingName) {
      showToastError(__("Ticket name is required.", "eventkoi-lite"));
      return false;
    }

    const invalidDates = ticketsToValidate.find((ticket) => {
      if (isEmptyNewTicket(ticket)) return false;
      if (!ticket.sale_start || !ticket.sale_end) return false;
      const start = Date.parse(ensureUtcZ(ticket.sale_start));
      const end = Date.parse(ensureUtcZ(ticket.sale_end));
      return Number.isFinite(start) && Number.isFinite(end) && end < start;
    });

    if (invalidDates) {
      showToastError(__("Ticket sales end date cannot be before the start date.", "eventkoi-lite"));
      return false;
    }

    return true;
  };

  const saveTickets = async (ticketsToSave) => {
    if (!event?.id || savingTickets) return;
    const filteredTickets = ticketsToSave.filter(
      (ticket) => !isEmptyNewTicket(ticket)
    );
    if (!validateTickets(filteredTickets)) {
      return;
    }

    setSavingTickets(true);
    try {
      // Commit any pending deletions first. Tolerate 404s (already gone).
      const pendingDeletes = deletedTicketIdsRef.current.slice();
      const succeededDeletes = [];
      for (const ticketId of pendingDeletes) {
        if (!ticketId) continue;
        try {
          await callLocalApi(`tickets/${ticketId}`, { method: "DELETE" });
          succeededDeletes.push(ticketId);
        } catch (error) {
          if (error?.code === "rest_post_invalid_id" || error?.data?.status === 404) {
            succeededDeletes.push(ticketId);
            continue;
          }
          throw error;
        }
      }
      if (succeededDeletes.length) {
        setDeletedTicketIds((prev) =>
          prev.filter((id) => !succeededDeletes.includes(id))
        );
      }

      const savedTickets = [];
      // Track persisted indices so a partial loop-failure can splice the
      // already-IDed tickets back into local state. Without this, retry
      // re-POSTs the successful-but-unacknowledged tickets → duplicates.
      const persistedIndexes = new Set();

      try {
        for (let index = 0; index < filteredTickets.length; index += 1) {
          const ticket = filteredTickets[index];
          const payload = {
            name: ticket.name,
            description: ticket.description || "",
            price: ticket.price !== "" ? parseFloat(ticket.price) : 0,
            currency: ticket.currency || DEFAULT_CURRENCY,
            quantity_available:
              ticket.quantity_available === "" ||
              ticket.quantity_available === null
                ? null
                : parseInt(ticket.quantity_available, 10),
            max_per_order:
              ticket.max_per_order === "" || ticket.max_per_order === null
                ? null
                : parseInt(ticket.max_per_order, 10),
            sale_start:
              ticket.sale_start === undefined || ticket.sale_start === null
                ? ""
                : ticket.sale_start,
            sale_end:
              ticket.sale_end === undefined || ticket.sale_end === null
                ? ""
                : ticket.sale_end,
            terms_conditions: ticket.terms_conditions || "",
            sort_order: index,
            status: ticket.status || "active",
          };

          if (ticket.id) {
            const response = await callLocalApi(`tickets/${ticket.id}`, {
              method: "PUT",
              data: payload,
            });
            savedTickets.push(normalizeTicket(response?.ticket || ticket));
            persistedIndexes.add(index);
          } else {
            const response = await callLocalApi(
              `events/${event.id}/tickets`,
              {
                method: "POST",
                data: payload,
              }
            );
            if (response?.ticket) {
              savedTickets.push(normalizeTicket(response.ticket));
              persistedIndexes.add(index);
            }
          }
        }
      } catch (loopError) {
        const remaining = filteredTickets
          .map((t, i) => (persistedIndexes.has(i) ? null : t))
          .filter(Boolean);
        setTickets([...savedTickets, ...remaining]);
        throw loopError;
      }

      setTickets(savedTickets);
      const storedExpanded =
        expandedStorageKey &&
        typeof window !== "undefined" &&
        window.localStorage
          ? window.localStorage.getItem(expandedStorageKey)
          : null;
      let parsedExpanded = null;
      if (storedExpanded) {
        try {
          parsedExpanded = JSON.parse(storedExpanded);
        } catch (e) {
          parsedExpanded = null;
        }
      }
      setExpandedTickets(
        savedTickets.reduce((acc, ticket) => {
          const storedValue =
            parsedExpanded &&
            Object.prototype.hasOwnProperty.call(
              parsedExpanded,
              String(ticket.id)
            )
              ? parsedExpanded[String(ticket.id)]
              : null;
          acc[ticket.id] = typeof storedValue === "boolean" ? storedValue : true;
          return acc;
        }, {})
      );
      return savedTickets;
    } catch (error) {
      if (error?.message === "Ticket name is required.") {
        return;
      }
      showToastError(__("Failed to save tickets.", "eventkoi-lite"));
      throw error;
    } finally {
      setSavingTickets(false);
    }
  };

  useEffect(() => {
    if (!registerBeforeSave) return;
    return registerBeforeSave(async () => {
      if (!event?.id) return;
      const currentTickets = ticketsRef.current;
      if (!currentTickets || currentTickets.length === 0) return;
      await saveTickets(currentTickets);
    });
  }, [registerBeforeSave, event?.id]);

  const saveTicketsRef = useRef(saveTickets);
  useEffect(() => {
    saveTicketsRef.current = saveTickets;
  });

  useEffect(() => {
    const eventId = event?.id;
    return () => {
      if (!eventId) return;
      const currentTickets = ticketsRef.current;
      if (!currentTickets || currentTickets.length === 0) return;
      Promise.resolve(saveTicketsRef.current(currentTickets)).catch(() => {});
    };
  }, [event?.id]);

  return (
    <div className="flex flex-col w-full gap-8">
      <Box container>
        <Heading level={3}>{__("Display options", "eventkoi-lite")}</Heading>

        {!event?.id && (
          <p className="text-sm text-muted-foreground">
            {__(
              "Save the event first before adding tickets.",
              "eventkoi-lite"
            )}
          </p>
        )}

        <div className="flex flex-col gap-6">
          <SettingToggle
            id="tickets_auto_create_account"
            label={__("Auto-create attendee account", "eventkoi-lite")}
            description={__(
              "Create a WordPress user when someone purchases a ticket.",
              "eventkoi-lite",
            )}
            checked={ticketSettings.auto_create_account}
            onCheckedChange={() => handleToggle("auto_create_account")}
          />

          <SettingToggle
            id="tickets_show_remaining"
            label={__("Show remaining tickets", "eventkoi-lite")}
            description={__(
              "Display the number of available tickets on the event page.",
              "eventkoi-lite",
            )}
            checked={ticketSettings.show_remaining}
            onCheckedChange={() => handleToggle("show_remaining")}
          />

          <div className="flex flex-col gap-4">
            <div className="grid gap-1">
              <Label className="font-medium">
                {__("Show unavailable tickets", "eventkoi-lite")}
              </Label>
              <p className="text-sm text-muted-foreground">
                {__(
                  "Choose which unavailable ticket types stay visible on the event page.",
                  "eventkoi-lite",
                )}
              </p>
            </div>

            <SettingToggle
              id="tickets_show_sold_out"
              label={__("Display sold out tickets", "eventkoi-lite")}
              checked={ticketSettings.show_sold_out}
              onCheckedChange={() => handleToggle("show_sold_out")}
            />

            <SettingToggle
              id="tickets_show_upcoming"
              label={__("Display upcoming tickets", "eventkoi-lite")}
              checked={ticketSettings.show_upcoming}
              onCheckedChange={() => handleToggle("show_upcoming")}
            />

            <SettingToggle
              id="tickets_show_ended"
              label={__("Display ended tickets", "eventkoi-lite")}
              checked={ticketSettings.show_ended}
              onCheckedChange={() => handleToggle("show_ended")}
            />
          </div>

          <div className="flex flex-col gap-2 max-w-[500px] pt-4">
            <Label className="font-medium" htmlFor="tickets-event-capacity">
              {__("Total ticket quantity per event", "eventkoi-lite")}
            </Label>
            <Input
              id="tickets-event-capacity"
              type="text"
              inputMode="numeric"
              pattern="[0-9]*"
              placeholder={__("Unlimited", "eventkoi-lite")}
              value={ticketSettings.event_capacity}
              onChange={handleCapacityChange}
              className="max-w-[200px]"
            />
            <p className="text-sm text-muted-foreground">
              {__(
                "Caps the combined tickets sold across all ticket types, for venues with a maximum capacity. Leave empty for unlimited.",
                "eventkoi-lite",
              )}
            </p>
          </div>

        </div>
      </Box>

      <Box container>
        <Heading level={3}>{__("Terms and conditions", "eventkoi-lite")}</Heading>

        <div className="flex flex-col gap-6">
          <div className="flex flex-col gap-2">
            <Label className="font-medium" htmlFor="tickets-terms-conditions">
              {__("Terms & conditions", "eventkoi-lite")}
            </Label>
            <Textarea
              id="tickets-terms-conditions"
              placeholder={__(
                "Add ticket terms and conditions here.",
                "eventkoi-lite",
              )}
              value={ticketSettings.terms_conditions}
              onChange={handleTextChange}
              className="min-h-[110px]"
            />
          </div>

          {ticketSettings.terms_conditions.trim().length > 0 && (
            <SettingToggle
              id="tickets_terms_conditions_required"
              label={__("Require agreement to terms", "eventkoi-lite")}
              description={__(
                "Show the terms as a checkbox the buyer must tick before checkout.",
                "eventkoi-lite",
              )}
              checked={ticketSettings.terms_conditions_required}
              onCheckedChange={() =>
                handleToggle("terms_conditions_required")
              }
            />
          )}

          <Separator />

          <div className="flex flex-col gap-2">
            <Label className="font-medium">
              {__("Agreement checkboxes", "eventkoi-lite")}
            </Label>
            <p className="text-sm text-muted-foreground">
              {__(
                "Separate consents shown as individual checkboxes at checkout, for example a privacy statement or refund policy. Required ones must be ticked before the buyer can continue.",
                "eventkoi-lite",
              )}
            </p>
            {(ticketSettings.agreements || []).map((agreement, index) => (
              <div
                key={agreement.id || index}
                className="flex flex-col gap-2 rounded-md border border-border p-3"
              >
                <Textarea
                  aria-label={__("Agreement text", "eventkoi-lite")}
                  placeholder={__(
                    "e.g. I have read and accept the privacy statement.",
                    "eventkoi-lite",
                  )}
                  value={agreement.text || ""}
                  onChange={(e) =>
                    handleAgreementChange(index, { text: e.target.value })
                  }
                  className="min-h-[60px]"
                />
                <div className="flex items-center justify-between gap-3">
                  <label className="flex items-center gap-2 text-sm">
                    <Switch
                      checked={agreement.required !== false}
                      onCheckedChange={(checked) =>
                        handleAgreementChange(index, { required: !!checked })
                      }
                      aria-label={__("Require this agreement", "eventkoi-lite")}
                    />
                    {__("Required", "eventkoi-lite")}
                  </label>
                  <Button
                    type="button"
                    variant="ghost"
                    size="icon"
                    onClick={() => handleRemoveAgreement(index)}
                    aria-label={__("Remove agreement", "eventkoi-lite")}
                  >
                    <Trash2 className="h-4 w-4" />
                  </Button>
                </div>
              </div>
            ))}
            <Button
              type="button"
              variant="outline"
              className="w-fit"
              onClick={handleAddAgreement}
            >
              <Plus className="mr-2 h-4 w-4" />
              {__("Add agreement", "eventkoi-lite")}
            </Button>
          </div>

        </div>
      </Box>

      <Box container>
        <Heading level={3}>{__("Create tickets", "eventkoi-lite")}</Heading>

        <div className="flex flex-col gap-6">
          <div className="flex flex-col gap-4">
            {loadingTickets && (
              <div className="text-sm text-muted-foreground">
                {__("Loading tickets…", "eventkoi-lite")}
              </div>
            )}

            {tickets.map((ticket, index) => {
              const key = ticket.id || ticket.tempId || index;
              const isExpanded = expandedTickets[key] ?? true;
              const isDragging = draggingIndex === index;
              const startDateRaw = ticket.sale_start
                ? getDateInTimezone(ensureUtcZ(ticket.sale_start), wpTz)
                : null;
              const endDateRaw = ticket.sale_end
                ? getDateInTimezone(ensureUtcZ(ticket.sale_end), wpTz)
                : null;
              const startDate =
                startDateRaw && isValid(startDateRaw) ? startDateRaw : null;
              const endDate = endDateRaw && isValid(endDateRaw) ? endDateRaw : null;

              return (
                <div
                  key={key}
                  className={cn(
                    "rounded-xl border border-input bg-white p-4",
                    isDragging && "opacity-60",
                    dragOverIndex === index &&
                      "border-primary/70 shadow-[0_0_0_1px_rgba(0,0,0,0.05)]"
                  )}
                  data-drag-item
                  onDragOver={handleDragOver(index)}
                  onDrop={handleDrop(index)}
                >
                  <div className="relative flex flex-wrap items-center justify-between gap-4">
                    <div className="flex items-center gap-3 min-w-0 flex-1">
                      <Button
                        variant="ghost"
                        size="icon"
                        className="h-7 w-7 text-foreground hover:text-foreground cursor-grab active:cursor-grabbing"
                        onClick={(e) => e.preventDefault()}
                        aria-label={__("Reorder ticket", "eventkoi-lite")}
                        title={__("Drag to reorder", "eventkoi-lite")}
                        draggable
                        onDragStart={handleDragStart(index)}
                        onDragEnd={handleDragEnd}
                      >
                        <GripVertical className="h-4 w-4" aria-hidden="true" />
                      </Button>
                      <Input
                        aria-label={__("Ticket name", "eventkoi-lite")}
                        placeholder={__("Enter ticket name", "eventkoi-lite")}
                        value={ticket.name || ""}
                        onChange={(e) =>
                          handleTicketChange(index, { name: e.target.value })
                        }
                        className="max-w-[260px] h-9 text-base border-foreground/40 focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:border-foreground"
                      />
                    </div>
                    <div className="flex items-center gap-3">
                      <div className="flex items-center gap-2">
                        <Switch
                          aria-label={__("Toggle ticket active", "eventkoi-lite")}
                          checked={ticket.status !== "inactive"}
                          onCheckedChange={(checked) =>
                            handleTicketChange(index, {
                              status: checked ? "active" : "inactive",
                            })
                          }
                          className="data-[state=checked]:bg-foreground"
                        />
                        <span className="text-xs text-muted-foreground">
                          {__("Active", "eventkoi-lite")}
                        </span>
                      </div>
                      <Button
                        variant="ghost"
                        size="icon"
                        className="h-7 w-7 text-foreground/90 hover:text-foreground"
                        onClick={() => toggleTicket(key)}
                        aria-expanded={isExpanded}
                        aria-label={isExpanded ? __("Collapse ticket", "eventkoi-lite") : __("Expand ticket", "eventkoi-lite")}
                      >
                        {isExpanded ? (
                          <ChevronUp className="h-4 w-4" aria-hidden="true" />
                        ) : (
                          <ChevronDown className="h-4 w-4" aria-hidden="true" />
                        )}
                      </Button>
                      <Button
                        variant="ghost"
                        size="icon"
                        className="h-7 w-7 text-foreground/90 hover:text-destructive"
                        onClick={() => handleDeleteTicket(ticket, index)}
                        aria-label={__("Delete ticket", "eventkoi-lite")}
                      >
                        <Trash2 className="h-4 w-4" aria-hidden="true" />
                      </Button>
                    </div>
                  </div>

                  {isExpanded && (
                    <div className="mt-6 grid gap-6 p-6">
                      <div className="flex flex-wrap items-start gap-16">
                        <div className="grid gap-2">
                          <Label htmlFor={`ticket-${key}-price`}>
                            {__("Price", "eventkoi-lite")}
                          </Label>
                        <InputGroup className="w-auto">
                          <InputGroupText>
                            {currencySymbol || globalCurrency}
                          </InputGroupText>
                          <InputGroupInput
                            id={`ticket-${key}-price`}
                            type="number"
                            min="0"
                            step="0.01"
                            className="w-[120px]"
                            value={ticket.price ?? ""}
                              onChange={(e) =>
                                handleTicketChange(index, {
                                  price: e.target.value,
                                })
                              }
                            />
                          </InputGroup>
                          <div className="inline-flex items-center gap-2 text-xs text-muted-foreground">
                            <span>{globalCurrency}</span>
                            <span aria-hidden="true">•</span>
                            {(globalSettings?.ticket_checkout_method || "stripe") === "woocommerce" ? (
                              <a
                                href={window?.eventkoi_params?.wc_settings_url || "/wp-admin/admin.php?page=wc-settings"}
                                className="underline underline-offset-2 hover:text-foreground"
                              >
                                {__("Change currency", "eventkoi-lite")}
                              </a>
                            ) : (
                              <Link
                                to="/settings/payments"
                                className="underline underline-offset-2 hover:text-foreground"
                              >
                                {__("Change currency", "eventkoi-lite")}
                              </Link>
                            )}
                          </div>
                        </div>
                        <div className="grid gap-2">
                          <Label htmlFor={`ticket-${key}-quantity`}>
                            {__("Quantity available", "eventkoi-lite")}
                          </Label>
                          <Input
                            id={`ticket-${key}-quantity`}
                            type="number"
                            min="0"
                            className="max-w-[120px]"
                            value={ticket.quantity_available ?? ""}
                            onChange={(e) =>
                              handleTicketChange(index, {
                                quantity_available: e.target.value,
                              })
                            }
                          />
                        </div>
                        <div className="grid gap-2">
                          <Label htmlFor={`ticket-${key}-max-per-order`}>
                            {__("Max per order", "eventkoi-lite")}
                          </Label>
                          <Input
                            id={`ticket-${key}-max-per-order`}
                            type="number"
                            min="0"
                            className="max-w-[120px]"
                            value={ticket.max_per_order ?? ""}
                            onChange={(e) =>
                              handleTicketChange(index, {
                                max_per_order: e.target.value,
                              })
                            }
                          />
                        </div>
                      </div>

                      <div className="grid grid-cols-1 gap-4">
                        <div className="flex flex-wrap items-center gap-4">
                          <div className="grid gap-2">
                            <div className="flex items-center justify-between gap-2 min-h-[18px]">
                              <Label>
                                {__("Ticket sales start", "eventkoi-lite")}
                              </Label>
                              {startDate ? (
                                <button
                                  type="button"
                                  className="text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground"
                                  onClick={() =>
                                    handleTicketChange(index, {
                                      sale_start: null,
                                    })
                                  }
                                >
                                  {__("Clear", "eventkoi-lite")}
                                </button>
                              ) : (
                                <span className="text-xs opacity-0">{"Clear"}</span>
                              )}
                            </div>
                            <div className="flex flex-wrap items-center gap-2">
                              <FloatingDatePicker
                                value={startDate || undefined}
                                wpTz={wpTz}
                                onChange={(pickedDate) => {
                                  if (!pickedDate) return;
                                  const startTime = startDate
                                    ? {
                                        h: startDate.getHours(),
                                        m: startDate.getMinutes(),
                                      }
                                    : { h: 9, m: 0 };
                                  const newStart = pickedDate.set({
                                    hour: startTime.h,
                                    minute: startTime.m,
                                    second: 0,
                                    millisecond: 0,
                                  });
                                  handleTicketChange(index, {
                                    sale_start: newStart
                                      .toUTC()
                                      .toISO({ suppressMilliseconds: true }),
                                  });
                                }}
                                className="disabled:bg-muted disabled:text-muted-foreground/40 disabled:cursor-not-allowed disabled:opacity-100"
                              />
                              <TimeInput
                                date={startDate || undefined}
                                wpTz={wpTz}
                                setDate={(utcDate) => {
                                  if (!utcDate || !startDate) return;
                                  const parsedUtc = DateTime.fromJSDate(utcDate, {
                                    zone: "utc",
                                  }).setZone(wpTz);
                                  const dtWall = DateTime.fromJSDate(startDate, {
                                    zone: wpTz,
                                  }).set({
                                    hour: parsedUtc.hour,
                                    minute: parsedUtc.minute,
                                    second: 0,
                                    millisecond: 0,
                                  });
                                  handleTicketChange(index, {
                                    sale_start: dtWall
                                      .toUTC()
                                      .toISO({ suppressMilliseconds: true }),
                                  });
                                }}
                                disabled={!startDate}
                              />
                            </div>
                          </div>

                          <MoveRight
                            className="w-6 h-6 text-muted-foreground mt-[22px]"
                            strokeWidth={1.5}
                          />

                          <div className="grid gap-2">
                            <div className="flex items-center justify-between gap-2 min-h-[18px]">
                              <Label>{__("Ticket sales end", "eventkoi-lite")}</Label>
                              {endDate ? (
                                <button
                                  type="button"
                                  className="text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground"
                                  onClick={() =>
                                    handleTicketChange(index, {
                                      sale_end: null,
                                    })
                                  }
                                >
                                  {__("Clear", "eventkoi-lite")}
                                </button>
                              ) : (
                                <span className="text-xs opacity-0">{"Clear"}</span>
                              )}
                            </div>
                            <div className="flex flex-wrap items-center gap-2">
                              <FloatingDatePicker
                                value={endDate || undefined}
                                wpTz={wpTz}
                                onChange={(pickedDate) => {
                                  if (!pickedDate) return;
                                  const endTime = endDate
                                    ? {
                                        h: endDate.getHours(),
                                        m: endDate.getMinutes(),
                                      }
                                    : { h: 17, m: 0 };
                                  const newEnd = pickedDate.set({
                                    hour: endTime.h,
                                    minute: endTime.m,
                                    second: 0,
                                    millisecond: 0,
                                  });
                                  handleTicketChange(index, {
                                    sale_end: newEnd
                                      .toUTC()
                                      .toISO({ suppressMilliseconds: true }),
                                  });
                                }}
                                className="disabled:bg-muted disabled:text-muted-foreground/40 disabled:cursor-not-allowed disabled:opacity-100"
                              />
                              <TimeInput
                                date={endDate || undefined}
                                wpTz={wpTz}
                                setDate={(utcDate) => {
                                  if (!utcDate || !endDate) return;
                                  const parsedUtc = DateTime.fromJSDate(utcDate, {
                                    zone: "utc",
                                  }).setZone(wpTz);
                                  const dtWall = DateTime.fromJSDate(endDate, {
                                    zone: wpTz,
                                  }).set({
                                    hour: parsedUtc.hour,
                                    minute: parsedUtc.minute,
                                    second: 0,
                                    millisecond: 0,
                                  });
                                  handleTicketChange(index, {
                                    sale_end: dtWall
                                      .toUTC()
                                      .toISO({ suppressMilliseconds: true }),
                                  });
                                }}
                                disabled={!endDate}
                              />
                            </div>
                          </div>
                        </div>
                        {startDate && endDate && endDate < startDate ? (
                          <p className="text-sm font-medium text-destructive mt-1">
                            {__(
                              "Ticket sales end date cannot be before the start date.",
                              "eventkoi-lite",
                            )}
                          </p>
                        ) : null}
                      </div>

                      <div className="grid gap-2">
                      <Label htmlFor={`ticket-${key}-description`}>
                        {__("Description", "eventkoi-lite")}
                      </Label>
                      <Input
                        id={`ticket-${key}-description`}
                        value={ticket.description || ""}
                        onChange={(e) =>
                          handleTicketChange(index, {
                            description: e.target.value,
                          })
                        }
                        className="max-w-[468px]"
                      />
                        <p className="text-sm text-muted-foreground">
                          {__(
                            "Give a short description of the ticket to attendees.",
                            "eventkoi-lite"
                          )}
                        </p>
                      </div>

                      <div className="grid gap-2">
                        <Label htmlFor={`ticket-${key}-terms`}>
                          {__("Additional terms & conditions", "eventkoi-lite")}
                        </Label>
                        <Textarea
                          id={`ticket-${key}-terms`}
                          value={ticket.terms_conditions || ""}
                          onChange={(e) =>
                            handleTicketChange(index, {
                              terms_conditions: e.target.value,
                          })
                        }
                        className="min-h-[110px] max-w-[468px]"
                      />
                        <p className="text-sm text-muted-foreground">
                          {__(
                            "Add additional terms & conditions that only apply to this ticket.",
                            "eventkoi-lite"
                          )}
                        </p>
                      </div>
                    </div>
                  )}
                </div>
              );
            })}

            <div className="flex items-center justify-between">
              <Button
                type="button"
                variant="ghost"
                onClick={handleAddTicket}
                disabled={!event?.id}
              >
                <Plus className="w-4 h-4 mr-2" aria-hidden="true" />
                {__("Add ticket", "eventkoi-lite")}
              </Button>
            </div>
        </div>
      </div>
    </Box>
    <ConfirmDialog
      open={!!deleteConfirmTarget}
      onOpenChange={(next) => {
        if (!next) setDeleteConfirmTarget(null);
      }}
      icon="delete"
      title={__("Delete this ticket?", "eventkoi-lite")}
      description={__(
        "This ticket will be removed from the event when you click Save. You can undo by leaving the page without saving.",
        "eventkoi-lite"
      )}
      confirmLabel={__("Remove", "eventkoi-lite")}
      onConfirm={() => {
        if (deleteConfirmTarget) {
          removeTicketRow(
            deleteConfirmTarget.ticket,
            deleteConfirmTarget.index
          );
        }
      }}
    />
  </div>
  );
}
