/**
 * Email Campaign Composer — modal Dialog + horizontal Stepper.
 *
 * GHL-aligned 3-step flow:
 *   1. Content   — template and subject line.
 *   2. Recipients & Sender — send-to (all / smart list / by tag) + the read-only sender.
 *   3. Schedule & Review   — send now / schedule / recurring, then a summary.
 *
 * The sender is **read-only** (one configured identity — not editable per
 * campaign; deliverability/brand), the unsubscribe footer is always included,
 * scheduling can't be set to a past date, and the recurring cadence is a plain
 * day count (7 / 14 / 30 presets provided).
 *
 * Fully controlled: templates, smart lists and the sender identity come in as
 * props, and `onSend` receives the composed campaign payload.
 */
import { type ReactElement, useEffect, useRef, useState } from "react";
import { ArrowUpRight, CalendarClock, Repeat, Send } from "lucide-react";
import { Button } from "./button";
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
  ComboboxTrigger,
  ComboboxValue,
} from "./combobox";
import { DatePicker } from "./date-picker";
import { RadioGroup, RadioGroupItem } from "./radio-group";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "./select";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogFooter,
} from "./dialog";
import { Step, StepItem, Stepper } from "./stepper";
import { TagInput } from "./tag-input";
import { TimePicker } from "./time-picker";
import { Field, FieldError, FieldLabel } from "./field";
import { Input } from "./input";
import { Label } from "./label";

const STEPS = ["Content", "Recipients", "Schedule"] as const;

/** Common cadences surfaced as one-click presets over the day-count input. */
const RECURRENCE_PRESETS: { label: string; days: number }[] = [
  { label: "Weekly", days: 7 },
  { label: "Fortnightly", days: 14 },
  { label: "Monthly", days: 30 },
];

/** Human AM/PM label for a stored 24h "HH:MM" value (any minute). */
const timeLabel = (v: string): string => {
  const [h, m] = v.split(":").map(Number);
  if (Number.isNaN(h) || Number.isNaN(m)) return v;
  const h12 = h % 12 === 0 ? 12 : h % 12;
  return `${h12}:${String(m).padStart(2, "0")} ${h < 12 ? "AM" : "PM"}`;
};

export interface CampaignTemplateOption {
  id: string;
  title: string;
  subject: string;
}

export interface CampaignSmartListOption {
  id: string;
  name: string;
  count: number;
}

export interface CampaignSenderIdentity {
  fromName: string;
  fromEmail: string;
}

export type CampaignRecipientMode = "all" | "smart_list" | "tags";
export type CampaignSendMode = "now" | "one_off" | "recurring";

/** Everything the composer collected, ready for the create-campaign API. */
export interface EmailCampaignPayload {
  name: string;
  templateId: string | null;
  subject: string;
  recipientMode: CampaignRecipientMode;
  smartListId: string | null;
  /** Selected tags when recipientMode is "tags" — a contact matching ANY is included. */
  tags: string[];
  sendMode: CampaignSendMode;
  /** Local date+time combined; undefined for send-now. */
  scheduledAt?: Date;
  /** Days between sends; only for recurring. */
  recurrenceIntervalDays?: number;
}

export interface EmailCampaignComposerProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  /** Templates the broker can start from — both System and Personal. */
  templates: CampaignTemplateOption[];
  /**
   * Typing in the template picker. `templates` is a capped first page, so anything past the cap
   * is only reachable by searching server-side — the picker filters nothing locally.
   */
  onTemplateSearch?: (term: string) => void;
  isTemplateLoading?: boolean;
  /** Saved Contacts segments the composer can send to. */
  smartLists: CampaignSmartListOption[];
  /** The company's configured sender identity (read-only display). */
  sender: CampaignSenderIdentity;
  onSend?: (payload: EmailCampaignPayload) => void;
  initialCampaignName?: string;
  /** Preselect a template (e.g. opened via a template card's "Create campaign"). */
  initialTemplateId?: string | null;
  /**
   * The selected template's name, for when it is not in `templates` — the list is a searched
   * page, so an edited campaign's template, or one carried in from a template card, is often
   * absent from it. Without this the trigger falls back to rendering the raw id.
   */
  initialTemplateName?: string;
  initialSubject?: string;
  initialRecipientMode?: CampaignRecipientMode;
  initialSmartListId?: string | null;
  /** The broker's tag library, for autocomplete when targeting by tag. */
  tagSuggestions?: string[];
  initialTags?: string[];
  initialSendMode?: CampaignSendMode;
  /** The campaign's current send time — seeds both the date and the time controls. */
  initialScheduledAt?: Date | null;
  initialRecurrenceIntervalDays?: number;
  /** Header text; edit flows pass the campaign's own heading. */
  title?: string;
  /** Final-step button; edit flows pass something other than "Confirm & Send". */
  submitLabel?: string;
  /**
   * Editing an existing campaign. Its content was snapshotted at creation, so the template is
   * fixed — and a campaign that already exists is saved, not sent.
   */
  isEdit?: boolean;
  /** Manage-smart-lists affordance (links out to Contacts in the app). */
  onManageSmartLists?: () => void;
}

/**
 * A send must be far enough out that the schedule is worth having; the backend rejects anything
 * closer, so the composer refuses it first rather than trading a picker for a 400.
 */
const MIN_SCHEDULE_LEAD_MS = 30 * 60 * 1000;

/** "HH:MM" in local time, snapped up to the next half hour. */
const toTimeValue = (date: Date): string =>
  `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;

export function EmailCampaignComposer({
  open,
  onOpenChange,
  templates,
  onTemplateSearch,
  isTemplateLoading = false,
  smartLists,
  sender,
  onSend,
  initialCampaignName = "",
  initialTemplateId,
  initialTemplateName = "",
  initialSubject = "",
  initialRecipientMode = "all",
  initialSmartListId = null,
  tagSuggestions = [],
  initialTags = [],
  initialSendMode = "now",
  initialScheduledAt = null,
  initialRecurrenceIntervalDays = 7,
  title = "New Campaign",
  submitLabel,
  isEdit = false,
  onManageSmartLists,
}: EmailCampaignComposerProps): ReactElement {
  const [activeStep, setActiveStep] = useState(0);

  // 1 — Content
  const [campaignName, setCampaignName] = useState(initialCampaignName);
  const [templateId, setTemplateId] = useState<string | null>(
    initialTemplateId ?? (templates.length > 0 ? templates[0].id : null),
  );
  const [templateLabel, setTemplateLabel] = useState(initialTemplateName);
  const [subject, setSubject] = useState(initialSubject);

  // A ref, not state: emptying the field must not re-run the effect and refill it under the
  // cursor. It is read only when the template changes, which is the moment that matters.
  const isSubjectOwnEdit = useRef(Boolean(initialSubject));

  // The one place the subject follows the template. Templates arrive after open, so neither the
  // initial useState nor the picker's change handler can do it alone: the handler fires only on a
  // user pick, and by the time the list lands the selection already exists — whether it came from
  // `initialTemplateId` or from defaulting to the first. The label is remembered separately
  // because searching replaces `templates` with rows that need not include the selection.
  useEffect(() => {
    if (templates.length === 0) return;
    const selected = templateId
      ? templates.find((t) => t.id === templateId)
      : templates[0];
    if (!selected) return;
    if (!templateId) setTemplateId(selected.id);
    setTemplateLabel(selected.title);
    if (!isSubjectOwnEdit.current) setSubject(selected.subject);
  }, [templates, templateId]);

  // 2 — Recipients (sender is read-only)
  const [recipientMode, setRecipientMode] =
    useState<CampaignRecipientMode>(initialRecipientMode);
  const [smartListId, setSmartListId] = useState<string | null>(
    initialSmartListId,
  );
  const [tags, setTags] = useState<string[]>(initialTags);

  // 3 — Schedule
  // An existing campaign is never edited into sending "now" — that is the detail sheet's Send now
  // button, which dispatches rather than rewriting the schedule to a minute out. A campaign that
  // still carries `now` while waiting to go out is inconsistent, so it opens as a one-off.
  const [sendMode, setSendMode] = useState<CampaignSendMode>(
    isEdit && initialSendMode === "now" ? "one_off" : initialSendMode,
  );
  const [scheduledDate, setScheduledDate] = useState<Date | undefined>(
    initialScheduledAt ?? undefined,
  );
  const [scheduledTime, setScheduledTime] = useState(
    initialScheduledAt ? toTimeValue(initialScheduledAt) : "09:00",
  );
  const [recurrenceIntervalDays, setRecurrenceIntervalDays] = useState(
    initialRecurrenceIntervalDays,
  );
  const [showErrors, setShowErrors] = useState(false);

  // Scheduling can't be set in the past — block earlier days in the calendar.
  const today = new Date();
  today.setHours(0, 0, 0, 0);

  const selectedTemplate = templates.find((t) => t.id === templateId);
  const isLastStep = activeStep === STEPS.length - 1;

  const goNext = (): void => {
    setShowErrors(false);
    setActiveStep((s) => Math.min(s + 1, STEPS.length - 1));
  };
  const goBack = (): void => {
    setShowErrors(false);
    setActiveStep((s) => Math.max(s - 1, 0));
  };

  const buildScheduledAt = (): Date | undefined => {
    if (sendMode === "now" || !scheduledDate) return undefined;
    const [h, m] = scheduledTime.split(":").map(Number);
    const at = new Date(scheduledDate);
    at.setHours(Number.isNaN(h) ? 9 : h, Number.isNaN(m) ? 0 : m, 0, 0);
    return at;
  };
  const scheduledAt = buildScheduledAt();

  const stepErrors: Record<string, string | undefined>[] = [
    {
      campaignName: campaignName.trim() ? undefined : "Enter a campaign name.",
      templateId: templateId ? undefined : "Select a template.",
      subject: subject.trim() ? undefined : "Enter a subject line.",
    },
    {
      smartListId:
        recipientMode === "smart_list" && !smartListId
          ? "Select a smart list."
          : undefined,
      tags:
        recipientMode === "tags" && tags.length === 0
          ? "Select at least one tag."
          : undefined,
    },
    {
      scheduledDate:
        sendMode !== "now" && !scheduledDate
          ? "Pick a send date."
          : scheduledAt &&
              scheduledAt.getTime() < Date.now() + MIN_SCHEDULE_LEAD_MS
            ? "Pick a time at least 30 minutes from now."
            : undefined,
      recurrenceIntervalDays:
        sendMode === "recurring" && !(recurrenceIntervalDays >= 1)
          ? "Enter how many days between sends."
          : undefined,
    },
  ];
  const activeErrors = stepErrors[activeStep];
  const activeHasErrors = Object.values(activeErrors).some(Boolean);
  const fieldError = (field: string): string | undefined =>
    showErrors ? activeErrors[field] : undefined;

  const handlePrimaryAction = (): void => {
    if (activeHasErrors) {
      setShowErrors(true);
      return;
    }
    if (isLastStep) {
      onSend?.({
        name: campaignName.trim(),
        templateId,
        subject: subject.trim(),
        recipientMode,
        smartListId: recipientMode === "smart_list" ? smartListId : null,
        tags: recipientMode === "tags" ? tags : [],
        sendMode,
        scheduledAt,
        recurrenceIntervalDays:
          sendMode === "recurring" ? recurrenceIntervalDays : undefined,
      });
      onOpenChange(false);
      setActiveStep(0);
      setShowErrors(false);
      return;
    }
    goNext();
  };

  const selectedSmartList = smartLists.find((s) => s.id === smartListId);
  let recipientSummary = "All clients (not opted out)";
  if (recipientMode === "smart_list") {
    recipientSummary = selectedSmartList
      ? `Smart list — ${selectedSmartList.name} · ${selectedSmartList.count.toLocaleString()} clients`
      : "Smart list — none selected";
  } else if (recipientMode === "tags") {
    recipientSummary = tags.length
      ? `Tags — ${tags.join(", ")} · match any`
      : "Tags — none selected";
  }

  const startDateLabel = scheduledDate
    ? scheduledDate.toLocaleDateString()
    : "the selected date";
  const recurrencePresetLabel = RECURRENCE_PRESETS.find(
    (p) => p.days === recurrenceIntervalDays,
  )?.label;
  const recurrenceLabel =
    recurrencePresetLabel ?? `Every ${recurrenceIntervalDays} days`;
  let scheduleSummary = "Sends immediately after you confirm";
  if (sendMode === "one_off") {
    scheduleSummary = `Scheduled for ${startDateLabel} at ${timeLabel(scheduledTime)}`;
  } else if (sendMode === "recurring") {
    scheduleSummary = `${recurrenceLabel}, from ${startDateLabel} at ${timeLabel(scheduledTime)}`;
  }

  const DELIVERY_LABELS: Record<CampaignSendMode, string> = {
    now: "Send now",
    one_off: "Scheduled",
    recurring: "Recurring",
  };
  const deliveryLabel = DELIVERY_LABELS[sendMode];

  const reviewRows: { label: string; value: string }[] = [
    { label: "Subject", value: subject || "—" },
    { label: "Template", value: selectedTemplate?.title ?? "None selected" },
    { label: "Recipients", value: recipientSummary },
    {
      label: "Sender",
      value: `${sender.fromName} · ${sender.fromEmail}`,
    },
    { label: "Schedule", value: scheduleSummary },
  ];

  return (
    <Dialog onOpenChange={onOpenChange} open={open}>
      <DialogContent size="2xl">
        <DialogHeader>
          <DialogTitle>{title}</DialogTitle>
        </DialogHeader>

        <div className="max-h-[60vh] overflow-y-auto px-1">
          <div className="flex flex-col gap-6">
            <Stepper activeStep={activeStep} orientation="horizontal">
              <Step>
                <StepItem description="Template & subject" label="Content" />
              </Step>
              <Step>
                <StepItem description="Audience & sender" label="Recipients" />
              </Step>
              <Step>
                <StepItem description="Send & review" label="Schedule" />
              </Step>
            </Stepper>

            {/* 1 — Content */}
            {activeStep === 0 && (
              <div className="flex flex-col gap-4">
                <Field data-invalid={Boolean(fieldError("campaignName"))}>
                  <FieldLabel>Campaign name</FieldLabel>
                  <Input
                    aria-invalid={
                      Boolean(fieldError("campaignName")) || undefined
                    }
                    onChange={(e) => {
                      setCampaignName(e.target.value);
                    }}
                    placeholder="e.g. Spring Refinance Push"
                    value={campaignName}
                  />
                  <FieldError>{fieldError("campaignName")}</FieldError>
                </Field>
                <Field data-invalid={Boolean(fieldError("templateId"))}>
                  <FieldLabel>Template</FieldLabel>
                  <Combobox
                    disabled={isEdit}
                    // Searching is server-side, so the popup must show exactly the rows it is
                    // given. The built-in filter matches the item *values* — here template ids —
                    // so it discarded every result whose id didn't contain the search text.
                    filter={null}
                    items={templates.map((t) => t.id)}
                    // Only the selection; the effect above owns the label and the subject.
                    onValueChange={(v) => {
                      setTemplateId((v as string | null) ?? null);
                    }}
                    value={templateId}
                  >
                    <ComboboxTrigger
                      aria-invalid={
                        Boolean(fieldError("templateId")) || undefined
                      }
                      className="w-full"
                    >
                      {/* The remembered label, not a lookup: the selected template is routinely
                          absent from a searched page, and Value falls back to the raw id. */}
                      <ComboboxValue placeholder="Select a template…">
                        {templateLabel}
                      </ComboboxValue>
                    </ComboboxTrigger>
                    <ComboboxContent>
                      <ComboboxInput
                        onChange={(e) => onTemplateSearch?.(e.target.value)}
                        placeholder="Search templates…"
                      />
                      <ComboboxList>
                        {(id: string) => {
                          const t = templates.find((x) => x.id === id);
                          if (!t) return null;
                          return (
                            <ComboboxItem key={id} value={id}>
                              <span className="flex w-full items-center justify-between gap-4">
                                <span>{t.title}</span>
                                <span className="truncate text-caption text-muted-foreground">
                                  {t.subject}
                                </span>
                              </span>
                            </ComboboxItem>
                          );
                        }}
                      </ComboboxList>
                      <ComboboxEmpty>
                        {isTemplateLoading
                          ? "Searching…"
                          : "No matching template."}
                      </ComboboxEmpty>
                    </ComboboxContent>
                  </Combobox>
                  {isEdit ? (
                    <span className="text-caption text-muted-foreground">
                      The content was snapshotted when this campaign was
                      created, so its template can&apos;t be swapped — create a
                      new campaign to use another.
                    </span>
                  ) : null}
                  <FieldError>{fieldError("templateId")}</FieldError>
                </Field>
                <Field data-invalid={Boolean(fieldError("subject"))}>
                  <FieldLabel>Subject line</FieldLabel>
                  <Input
                    aria-invalid={Boolean(fieldError("subject")) || undefined}
                    onChange={(e) => {
                      setSubject(e.target.value);
                      // Emptying it hands the field back to the template.
                      isSubjectOwnEdit.current =
                        e.target.value.trim().length > 0;
                    }}
                    placeholder="e.g. Your refinance options this month"
                    value={subject}
                  />
                  <FieldError>{fieldError("subject")}</FieldError>
                </Field>
              </div>
            )}

            {/* 2 — Recipients & Sender */}
            {activeStep === 1 && (
              <div className="flex flex-col gap-4">
                <Field>
                  <FieldLabel>Send to</FieldLabel>
                  <RadioGroup
                    className="flex flex-row flex-wrap gap-x-6 gap-y-2"
                    onValueChange={(v) => {
                      setRecipientMode(v as CampaignRecipientMode);
                    }}
                    value={recipientMode}
                  >
                    <span className="flex items-center gap-2">
                      <RadioGroupItem
                        id="campaign-recipients-all"
                        value="all"
                      />
                      <Label
                        className="cursor-pointer text-body-small font-normal"
                        htmlFor="campaign-recipients-all"
                      >
                        All clients
                      </Label>
                    </span>
                    <span className="flex items-center gap-2">
                      <RadioGroupItem
                        id="campaign-recipients-smart-list"
                        value="smart_list"
                      />
                      <Label
                        className="cursor-pointer text-body-small font-normal"
                        htmlFor="campaign-recipients-smart-list"
                      >
                        Smart list
                      </Label>
                    </span>
                    <span className="flex items-center gap-2">
                      <RadioGroupItem
                        id="campaign-recipients-tags"
                        value="tags"
                      />
                      <Label
                        className="cursor-pointer text-body-small font-normal"
                        htmlFor="campaign-recipients-tags"
                      >
                        By tag
                      </Label>
                    </span>
                  </RadioGroup>
                </Field>

                {recipientMode === "smart_list" && (
                  <Field data-invalid={Boolean(fieldError("smartListId"))}>
                    <FieldLabel>Smart list</FieldLabel>
                    <Select
                      items={smartLists.map((s) => ({
                        value: s.id,
                        label: `${s.name} · ${s.count.toLocaleString()} clients`,
                      }))}
                      onValueChange={(v) => {
                        setSmartListId(v as string);
                      }}
                      value={smartListId ?? ""}
                    >
                      <SelectTrigger
                        aria-invalid={
                          Boolean(fieldError("smartListId")) || undefined
                        }
                      >
                        <SelectValue placeholder="Select a smart list…" />
                      </SelectTrigger>
                      <SelectContent>
                        {smartLists.map((s) => (
                          <SelectItem key={s.id} value={s.id}>
                            <span className="flex w-full items-center justify-between gap-4">
                              <span>{s.name}</span>
                              <span className="text-caption tabular-nums text-muted-foreground">
                                {s.count.toLocaleString()} contacts
                              </span>
                            </span>
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <div className="flex items-center justify-between gap-2">
                      <span className="text-caption text-muted-foreground">
                        Smart lists are saved client segments — create and edit
                        them in Clients.
                      </span>
                      <Button
                        className="h-auto shrink-0 gap-1 p-0 text-caption"
                        onClick={onManageSmartLists}
                        size="sm"
                        variant="link"
                      >
                        Manage in Clients
                        <ArrowUpRight className="size-3.5" />
                      </Button>
                    </div>
                    <FieldError>{fieldError("smartListId")}</FieldError>
                  </Field>
                )}

                {recipientMode === "tags" && (
                  <Field data-invalid={Boolean(fieldError("tags"))}>
                    <FieldLabel>Tags</FieldLabel>
                    <TagInput
                      value={tags}
                      onChange={setTags}
                      suggestions={tagSuggestions}
                      allowCreate={false}
                      placeholder="Select tags…"
                      aria-label="Select tags to target"
                    />
                    <span className="text-caption text-muted-foreground">
                      Contacts with any of the selected tags will receive this
                      campaign.
                    </span>
                    <FieldError>{fieldError("tags")}</FieldError>
                  </Field>
                )}

                {/* Sender is a single configured identity — read-only here, not
                    editable per campaign (deliverability + brand). */}
                <Field>
                  <FieldLabel>Sender</FieldLabel>
                  <div className="flex items-center justify-between gap-3 border border-border bg-muted/40 px-3 py-2.5">
                    <div className="flex min-w-0 flex-col">
                      <span className="truncate text-body-small font-medium text-foreground">
                        {sender.fromName}
                      </span>
                      <span className="truncate text-caption text-muted-foreground">
                        {sender.fromEmail}
                      </span>
                    </div>
                    <span className="shrink-0 text-caption text-muted-foreground">
                      Configured in settings
                    </span>
                  </div>
                </Field>
              </div>
            )}

            {/* 3 — Schedule & Review */}
            {activeStep === 2 && (
              <div className="flex flex-col gap-5">
                <Field>
                  <FieldLabel>Delivery</FieldLabel>
                  <RadioGroup
                    className="flex flex-row flex-wrap gap-x-6 gap-y-2"
                    onValueChange={(v) => {
                      setSendMode(v as CampaignSendMode);
                    }}
                    value={sendMode}
                  >
                    {isEdit ? null : (
                      <span className="flex items-center gap-2">
                        <RadioGroupItem id="campaign-send-now" value="now" />
                        <Label
                          className="cursor-pointer text-body-small font-normal"
                          htmlFor="campaign-send-now"
                        >
                          Send now
                        </Label>
                      </span>
                    )}
                    <span className="flex items-center gap-2">
                      <RadioGroupItem
                        id="campaign-send-one-off"
                        value="one_off"
                      />
                      <Label
                        className="cursor-pointer text-body-small font-normal"
                        htmlFor="campaign-send-one-off"
                      >
                        Schedule
                      </Label>
                    </span>
                    <span className="flex items-center gap-2">
                      <RadioGroupItem
                        id="campaign-send-recurring"
                        value="recurring"
                      />
                      <Label
                        className="cursor-pointer text-body-small font-normal"
                        htmlFor="campaign-send-recurring"
                      >
                        Recurring
                      </Label>
                    </span>
                  </RadioGroup>
                </Field>

                {sendMode === "recurring" && (
                  <Field
                    data-invalid={Boolean(fieldError("recurrenceIntervalDays"))}
                  >
                    <FieldLabel>Repeat every</FieldLabel>
                    <div className="flex flex-wrap items-center gap-2">
                      <div className="flex items-center gap-2">
                        <Input
                          aria-invalid={
                            Boolean(fieldError("recurrenceIntervalDays")) ||
                            undefined
                          }
                          className="w-24 tabular-nums"
                          min={1}
                          onChange={(e) => {
                            const days = Number(e.target.value);
                            if (Number.isInteger(days) && days >= 1) {
                              setRecurrenceIntervalDays(days);
                            }
                          }}
                          type="number"
                          value={recurrenceIntervalDays}
                        />
                        <span className="text-body-small text-muted-foreground">
                          days
                        </span>
                      </div>
                      <div className="flex gap-1.5">
                        {RECURRENCE_PRESETS.map((preset) => (
                          <Button
                            key={preset.days}
                            onClick={() => {
                              setRecurrenceIntervalDays(preset.days);
                            }}
                            size="sm"
                            type="button"
                            variant={
                              recurrenceIntervalDays === preset.days
                                ? "secondary"
                                : "outline"
                            }
                          >
                            {preset.label}
                          </Button>
                        ))}
                      </div>
                    </div>
                    <FieldError>
                      {fieldError("recurrenceIntervalDays")}
                    </FieldError>
                  </Field>
                )}

                {sendMode !== "now" && (
                  <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
                    <Field data-invalid={Boolean(fieldError("scheduledDate"))}>
                      <FieldLabel>
                        {sendMode === "one_off" ? "Send date" : "Start date"}
                      </FieldLabel>
                      <DatePicker
                        calendarProps={{ disabled: { before: today } }}
                        className={
                          fieldError("scheduledDate")
                            ? "border-destructive"
                            : undefined
                        }
                        onChange={setScheduledDate}
                        value={scheduledDate}
                      />
                      <FieldError>{fieldError("scheduledDate")}</FieldError>
                    </Field>
                    <Field>
                      <FieldLabel>
                        {sendMode === "one_off" ? "Send time" : "Start time"}
                      </FieldLabel>
                      <TimePicker
                        minuteStep={30}
                        onChange={setScheduledTime}
                        selectable
                        value={scheduledTime}
                      />
                    </Field>
                  </div>
                )}

                <div className="flex flex-col gap-2">
                  <span className="text-overline text-muted-foreground">
                    Review
                  </span>
                  <div className="border border-border">
                    <div className="flex items-start justify-between gap-3 border-b border-border bg-muted/40 px-4 py-3">
                      <div className="flex items-center gap-2">
                        <Send className="size-4 shrink-0 text-primary" />
                        <span className="text-body-medium font-semibold text-foreground">
                          {campaignName || "Untitled campaign"}
                        </span>
                      </div>
                      <span className="flex items-center gap-1.5 whitespace-nowrap text-caption text-muted-foreground">
                        {sendMode === "recurring" ? (
                          <Repeat className="size-3.5" />
                        ) : (
                          <CalendarClock className="size-3.5" />
                        )}
                        {deliveryLabel}
                      </span>
                    </div>
                    <dl className="divide-y divide-border">
                      {reviewRows.map((row) => (
                        <div
                          className="grid grid-cols-[112px_1fr] gap-4 px-4 py-2.5"
                          key={row.label}
                        >
                          <dt className="text-caption text-muted-foreground">
                            {row.label}
                          </dt>
                          <dd className="text-body-small text-foreground">
                            {row.value}
                          </dd>
                        </div>
                      ))}
                    </dl>
                  </div>
                </div>
              </div>
            )}
          </div>
        </div>

        <DialogFooter className="justify-between">
          <Button
            disabled={activeStep === 0}
            onClick={goBack}
            variant="outline"
          >
            Back
          </Button>
          <Button onClick={handlePrimaryAction}>
            {isLastStep ? (submitLabel ?? "Confirm & Send") : "Next"}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

export default EmailCampaignComposer;
