const { useEffect: useEffectPrimitive, useState: useStatePrimitive } = React;

function useConstructProSnapshot(initialSnapshot) {
  const [snapshot, setSnapshot] = useStatePrimitive(initialSnapshot || window.ConstructProData.getState());
  useEffectPrimitive(() => window.ConstructProData.subscribe(setSnapshot), []);
  return snapshot;
}

const toneClasses = {
  success: "bg-success/10 text-green-700 border-green-200",
  warning: "bg-amber-100 text-amber-800 border-amber-200",
  danger: "bg-red-100 text-red-800 border-red-200",
  info: "bg-primary-fixed text-primary border-primary-fixed",
  neutral: "bg-surface-container text-on-surface-variant border-outline-variant",
  orange: "bg-primary-fixed text-primary border-primary-fixed"
};

function Card({ children, className = "", pad = "p-5" }) {
  return <div className={`border border-outline-variant bg-surface-container-lowest ${pad} ${className}`} style={{ borderRadius: 16, boxShadow: "0 1px 2px rgba(11, 28, 48, 0.04)" }}>{children}</div>;
}

function Button({ children, variant = "primary", className = "", ...props }) {
  const variants = {
    primary: "bg-primary-container text-on-primary hover:bg-primary",
    secondary: "bg-primary-fixed text-primary hover:bg-surface-container",
    ghost: "bg-transparent text-on-surface-variant hover:bg-surface-container",
    danger: "bg-error text-white hover:bg-red-800"
  };
  return (
    <button className={`inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2.5 text-sm font-semibold transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${variants[variant]} ${className}`} {...props}>
      {children}
    </button>
  );
}

function Badge({ tone = "neutral", children, className = "" }) {
  return <span className={`inline-flex items-center rounded-full border px-2.5 py-1 text-xs font-bold ${toneClasses[tone] || toneClasses.neutral} ${className}`}>{children}</span>;
}

function CountdownBadge({ remainingSec = 0, durationSec = 30 }) {
  const total = Math.max(1, Number(durationSec) || 30);
  const remaining = Math.max(0, Number(remainingSec) || 0);
  const progress = Math.round((remaining / total) * 100);
  const urgent = remaining <= 10;
  const ended = remaining <= 0;
  return (
    <span
      className={`countdown-chip ${urgent ? "is-urgent" : ""} ${ended ? "is-ended" : ""}`}
      style={{ "--timer-progress": progress }}
      aria-label={ended ? "Request expired" : `${remaining} seconds left`}
    >
      {ended ? "Expired" : `${remaining}s left`}
    </span>
  );
}

function StatCard({ title, value, trend, icon, tone = "orange" }) {
  return (
    <Card>
      <div className="flex items-start justify-between gap-3">
        <div>
          <p className="mb-1 text-sm font-semibold text-text-muted">{title}</p>
          <h3 className="text-3xl font-bold text-text-primary">{value}</h3>
          {trend && <p className="mt-2 text-sm text-text-muted">{trend}</p>}
        </div>
        {icon && <div className={`flex h-9 w-9 items-center justify-center rounded-lg ${toneClasses[tone]?.split(" ").slice(0, 2).join(" ") || "bg-primary-fixed text-primary"}`}>{icon}</div>}
      </div>
    </Card>
  );
}

function PageHeader({ title, subtitle, actions }) {
  return (
    <div className="mb-6 flex flex-col gap-4 md:flex-row md:items-end md:justify-between">
      <div>
        <h1 className="text-2xl font-bold tracking-tight text-text-primary">{title}</h1>
        {subtitle && <p className="mt-1 text-sm text-text-muted">{subtitle}</p>}
      </div>
      {actions && <div className="flex flex-wrap items-center gap-2">{actions}</div>}
    </div>
  );
}

function ProgressBar({ value, tone = "orange" }) {
  const color = tone === "success" ? "bg-success" : tone === "danger" ? "bg-danger" : "bg-cta";
  return (
    <div className="dashboard-progress-track">
      <div className={`dashboard-progress-fill ${color === "bg-success" ? "!bg-success" : color === "bg-danger" ? "!bg-error" : ""}`} style={{ width: `${Math.max(0, Math.min(100, value || 0))}%` }} />
    </div>
  );
}

function SectionTitle({ title, subtitle }) {
  return (
    <div className="mb-4">
      <h2 className="text-lg font-bold text-text-primary">{title}</h2>
      {subtitle && <p className="text-sm text-text-muted">{subtitle}</p>}
    </div>
  );
}

function getRowMenuPosition(anchorEl, menuEl) {
  if (!anchorEl) return null;
  const rect = anchorEl.getBoundingClientRect();
  const menuHeight = menuEl?.offsetHeight || 88;
  const menuWidth = menuEl?.offsetWidth || 152;
  const gap = 4;
  const pad = 8;
  let top = rect.bottom + gap;
  let left = rect.right - menuWidth;
  if (top + menuHeight > window.innerHeight - pad) {
    top = Math.max(pad, rect.top - menuHeight - gap);
  }
  if (left < pad) left = pad;
  if (left + menuWidth > window.innerWidth - pad) {
    left = Math.max(pad, window.innerWidth - menuWidth - pad);
  }
  return { top, left };
}

function useFloatingRowMenu(anchorRef, open) {
  const menuRef = React.useRef(null);
  const [menuStyle, setMenuStyle] = React.useState(null);

  const updatePosition = React.useCallback(() => {
    const anchor = anchorRef.current?.querySelector("button");
    if (!anchor) return;
    const next = getRowMenuPosition(anchor, menuRef.current);
    if (!next) return;
    setMenuStyle((current) => {
      if (current && current.top === next.top && current.left === next.left) return current;
      return next;
    });
  }, [anchorRef]);

  React.useLayoutEffect(() => {
    if (!open) {
      setMenuStyle(null);
      return undefined;
    }
    updatePosition();
    window.addEventListener("resize", updatePosition);
    window.addEventListener("scroll", updatePosition, true);
    return () => {
      window.removeEventListener("resize", updatePosition);
      window.removeEventListener("scroll", updatePosition, true);
    };
  }, [open, updatePosition]);

  return { menuRef, menuStyle, setMenuStyle };
}

function RowStatusPicker({
  status,
  statusLabel,
  tone = "neutral",
  onChange,
  readOnly = false,
  readOnlyTitle = "Status rolls up from subtasks"
}) {
  const [open, setOpen] = React.useState(false);
  const [note, setNote] = React.useState("");
  const ref = React.useRef(null);
  const { menuRef, menuStyle, setMenuStyle } = useFloatingRowMenu(ref, open);
  const statuses = window.ConstructProData?.statuses || [];

  React.useEffect(() => {
    if (!open) return;
    const closeOnOutside = (event) => {
      if (ref.current?.contains(event.target)) return;
      if (menuRef.current?.contains(event.target)) return;
      setOpen(false);
    };
    const closeOnEscape = (event) => {
      if (event.key === "Escape") setOpen(false);
    };
    document.addEventListener("click", closeOnOutside);
    document.addEventListener("keydown", closeOnEscape);
    return () => {
      document.removeEventListener("click", closeOnOutside);
      document.removeEventListener("keydown", closeOnEscape);
    };
  }, [open, menuRef]);

  React.useEffect(() => {
    if (!open) setNote("");
  }, [open]);

  const pickStatus = (nextStatus) => {
    if (nextStatus === status) {
      setOpen(false);
      setMenuStyle(null);
      return;
    }
    onChange?.(nextStatus, note.trim() || undefined);
    setNote("");
    setOpen(false);
    setMenuStyle(null);
  };

  const toggleOpen = (event) => {
    event.stopPropagation();
    if (readOnly || !onChange) return;
    if (open) {
      setOpen(false);
      setMenuStyle(null);
      return;
    }
    setMenuStyle(getRowMenuPosition(event.currentTarget, null));
    setOpen(true);
  };

  if (readOnly || !onChange) {
    return (
      <Badge tone={tone} className="work-item-row__status-badge" title={readOnlyTitle}>
        {statusLabel}
      </Badge>
    );
  }

  const menu = open ? (
    <div
      ref={menuRef}
      className="row-status-picker__dropdown row-action-menu__dropdown--fixed"
      style={menuStyle || undefined}
      role="listbox"
      aria-label="Change status"
      onClick={(event) => event.stopPropagation()}
    >
      {statuses.map((entry) => (
        <button
          key={entry.id}
          type="button"
          role="option"
          aria-selected={entry.id === status}
          className={`row-status-picker__option${entry.id === status ? " row-status-picker__option--active" : ""}`}
          onClick={(event) => {
            event.stopPropagation();
            pickStatus(entry.id);
          }}
        >
          <span className={`row-status-picker__dot row-status-picker__dot--${entry.id}`} aria-hidden="true" />
          <span className="row-status-picker__option-label">{entry.label}</span>
          {entry.id === status ? <span className="row-status-picker__check" aria-hidden="true">✓</span> : null}
        </button>
      ))}
      <div className="row-status-picker__note">
        <input
          type="text"
          value={note}
          placeholder="Note (optional)"
          aria-label="Status note"
          onClick={(event) => event.stopPropagation()}
          onChange={(event) => setNote(event.target.value)}
          onKeyDown={(event) => {
            if (event.key === "Enter") {
              event.preventDefault();
              event.stopPropagation();
            }
          }}
        />
      </div>
    </div>
  ) : null;

  return (
    <div className={`row-status-picker${open ? " row-status-picker--open" : ""}`} ref={ref} onClick={(event) => event.stopPropagation()}>
      <button
        type="button"
        className={`row-status-picker__trigger work-item-row__status-badge ${toneClasses[tone] || toneClasses.neutral}`}
        onClick={toggleOpen}
        aria-label={`Status: ${statusLabel}. Click to change`}
        aria-expanded={open}
        aria-haspopup="listbox"
      >
        <span className="row-status-picker__label">{statusLabel}</span>
        <span className="row-status-picker__chevron" aria-hidden="true" />
      </button>
      {menu ? ReactDOM.createPortal(menu, document.body) : null}
    </div>
  );
}

function RowActionMenu({ onEdit, onDelete, editLabel = "Edit", deleteLabel = "Delete" }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  const { menuRef, menuStyle, setMenuStyle } = useFloatingRowMenu(ref, open);

  React.useEffect(() => {
    if (!open) return;
    const closeOnOutside = (event) => {
      if (ref.current?.contains(event.target)) return;
      if (menuRef.current?.contains(event.target)) return;
      setOpen(false);
    };
    const closeOnEscape = (event) => {
      if (event.key === "Escape") setOpen(false);
    };
    document.addEventListener("click", closeOnOutside);
    document.addEventListener("keydown", closeOnEscape);
    return () => {
      document.removeEventListener("click", closeOnOutside);
      document.removeEventListener("keydown", closeOnEscape);
    };
  }, [open, menuRef]);

  const toggleOpen = (event) => {
    event.stopPropagation();
    if (open) {
      setOpen(false);
      setMenuStyle(null);
      return;
    }
    setMenuStyle(getRowMenuPosition(event.currentTarget, null));
    setOpen(true);
  };

  const menu = open ? (
    <div
      ref={menuRef}
      className="row-action-menu__dropdown row-action-menu__dropdown--fixed"
      style={menuStyle || undefined}
      role="menu"
    >
      <button
        type="button"
        role="menuitem"
        className="row-action-menu__item"
        onClick={(event) => {
          event.stopPropagation();
          setOpen(false);
          onEdit();
        }}
      >
        <Icons.edit size={16} />
        {editLabel}
      </button>
      <button
        type="button"
        role="menuitem"
        className="row-action-menu__item row-action-menu__item--danger"
        onClick={(event) => {
          event.stopPropagation();
          setOpen(false);
          onDelete();
        }}
      >
        <Icons.trash size={16} />
        {deleteLabel}
      </button>
    </div>
  ) : null;

  return (
    <div className="row-action-menu" ref={ref}>
      <button
        type="button"
        className="m3-icon-button"
        onClick={toggleOpen}
        aria-label="More actions"
        aria-expanded={open}
        aria-haspopup="menu"
      >
        <Icons.moreVert size={18} />
      </button>
      {menu ? ReactDOM.createPortal(menu, document.body) : null}
    </div>
  );
}

function RowAddControl({ label, ariaLabel, onClick, options, compact = false }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  const { menuRef, menuStyle, setMenuStyle } = useFloatingRowMenu(ref, open);
  const hasMenu = Array.isArray(options) && options.length > 1;

  React.useEffect(() => {
    if (!open) return;
    const closeOnOutside = (event) => {
      if (ref.current?.contains(event.target)) return;
      if (menuRef.current?.contains(event.target)) return;
      setOpen(false);
    };
    const closeOnEscape = (event) => {
      if (event.key === "Escape") setOpen(false);
    };
    document.addEventListener("click", closeOnOutside);
    document.addEventListener("keydown", closeOnEscape);
    return () => {
      document.removeEventListener("click", closeOnOutside);
      document.removeEventListener("keydown", closeOnEscape);
    };
  }, [open, menuRef]);

  const handleDirectClick = (event) => {
    event.stopPropagation();
    onClick?.(event);
  };

  const handleMenuToggle = (event) => {
    event.stopPropagation();
    if (open) {
      setOpen(false);
      setMenuStyle(null);
      return;
    }
    setMenuStyle(getRowMenuPosition(event.currentTarget, null));
    setOpen(true);
  };

  const buttonClass = compact ? "row-add-button row-add-button--compact" : "row-add-button";

  if (hasMenu) {
    const menu = open ? (
      <div
        ref={menuRef}
        className="row-action-menu__dropdown row-action-menu__dropdown--add row-action-menu__dropdown--fixed"
        style={menuStyle || undefined}
        role="menu"
      >
        {options.map((option) => (
          <button
            key={option.label}
            type="button"
            role="menuitem"
            className="row-action-menu__item"
            onClick={(event) => {
              event.stopPropagation();
              setOpen(false);
              option.onClick?.(event);
            }}
          >
            <Icons.plus size={16} />
            {option.label}
          </button>
        ))}
      </div>
    ) : null;

    return (
      <div className="row-action-menu" ref={ref}>
        <button
          type="button"
          className={buttonClass}
          onClick={handleMenuToggle}
          aria-label={ariaLabel || `Add ${label}`}
          aria-expanded={open}
          aria-haspopup="menu"
          title={ariaLabel || `Add ${label}`}
        >
          {!compact ? <Icons.plus size={16} /> : null}
          <span className="row-add-button__label">{label}</span>
          {hasMenu ? <span className="row-add-button__chevron" aria-hidden="true">▾</span> : null}
        </button>
        {menu ? ReactDOM.createPortal(menu, document.body) : null}
      </div>
    );
  }

  return (
    <button
      type="button"
      className={buttonClass}
      onClick={handleDirectClick}
      aria-label={ariaLabel || `Add ${label}`}
      title={ariaLabel || `Add ${label}`}
    >
      {!compact ? <Icons.plus size={16} /> : null}
      <span className="row-add-button__label">{label}</span>
    </button>
  );
}

function clampNumber(value, min, max, fallback) {
  const number = Number(value);
  if (!Number.isFinite(number)) return fallback;
  return Math.min(max, Math.max(min, Math.round(number)));
}

function normalizeDurationAmount(value, unit) {
  return unit === "min" ? clampNumber(value, 1, 30, 1) : clampNumber(value, 10, 120, 30);
}

function durationToSeconds(value, unit) {
  const amount = normalizeDurationAmount(value, unit);
  return unit === "min" ? amount * 60 : amount;
}

const DURATION_AMOUNTS = {
  sec: [10, 20, 30, 45, 60, 90, 120],
  min: [1, 2, 5, 10, 15, 30]
};

function closestDurationAmount(value, unit) {
  const safeValue = normalizeDurationAmount(value, unit);
  const options = DURATION_AMOUNTS[unit] || DURATION_AMOUNTS.sec;
  return options.reduce((best, option) => (
    Math.abs(option - safeValue) < Math.abs(best - safeValue) ? option : best
  ), options[0]);
}

const DURATION_PRESETS = [
  { label: "30 sec", seconds: 30 },
  { label: "1 min", seconds: 60 },
  { label: "5 min", seconds: 300 }
];

function formatDurationLabel(seconds) {
  const safeSeconds = Math.max(10, Number(seconds) || 30);
  if (safeSeconds >= 60 && safeSeconds % 60 === 0) {
    const minutes = safeSeconds / 60;
    return `${minutes} ${minutes === 1 ? "minute" : "minutes"}`;
  }
  return `${safeSeconds} seconds`;
}

function AlertLauncher({ compact = false }) {
  const snapshot = useConstructProSnapshot();
  const [durationAmount, setDurationAmount] = React.useState(30);
  const [durationUnit, setDurationUnit] = React.useState("sec");
  const [roles, setRoles] = React.useState(() => [...window.ConstructProData.roles]);
  const [context, setContext] = React.useState("Quick update");
  const [workItemId, setWorkItemId] = React.useState("");
  const [numberOfPhotos, setNumberOfPhotos] = React.useState(1);
  const [floor, setFloor] = React.useState("");
  const taskOptions = (snapshot.workItems || []).filter((item) => item.type !== "phase");
  const contextId = "alert-context";
  const durationId = "alert-duration-amount";
  const durationUnitId = "alert-duration-unit";
  const workItemSelectId = "alert-work-item";
  const photoCountId = "alert-photo-count";
  const floorId = "alert-floor";
  const normalizedDurationAmount = normalizeDurationAmount(durationAmount, durationUnit);
  const durationSec = durationToSeconds(normalizedDurationAmount, durationUnit);

  const toggleRole = (role) => {
    setRoles((current) => current.includes(role) ? current.filter((item) => item !== role) : [...current, role]);
  };

  const updateDurationUnit = (nextUnit) => {
    setDurationAmount((current) => {
      const currentAmount = normalizeDurationAmount(current, durationUnit);
      const seconds = durationToSeconds(currentAmount, durationUnit);
      return nextUnit === "min" ? closestDurationAmount(Math.ceil(seconds / 60), "min") : closestDurationAmount(seconds, "sec");
    });
    setDurationUnit(nextUnit);
  };

  const setDurationPreset = (seconds) => {
    if (seconds >= 60 && seconds % 60 === 0) {
      setDurationUnit("min");
      setDurationAmount(seconds / 60);
      return;
    }
    setDurationUnit("sec");
    setDurationAmount(seconds);
  };

  const submit = () => {
    const linkedWork = taskOptions.find((item) => item.id === workItemId);
    const requiredPhotos = Math.max(1, Number(numberOfPhotos) || 1);
    window.ConstructProData.triggerAlert({
      title: linkedWork ? `Verify: ${linkedWork.title}` : "Quick Site Update",
      durationSec,
      roles,
      context: context || linkedWork?.title || snapshot.project.name,
      workItemId: workItemId || null,
      evidenceRequired: ["Photo", "Checklist", "Geofence timestamp"],
      numberOfPhotos: requiredPhotos,
      floor: floor.trim()
    });
  };

  return (
    <Card className={compact ? "" : "mb-6"}>
      <SectionTitle
        title="Send Site Verification Request"
        subtitle="Ask selected site roles for a time-bound photo and checklist update."
      />

      <div className="mt-4 space-y-4">
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
          <label className="block" htmlFor={workItemSelectId}>
            <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Linked work</span>
            <select id={workItemSelectId} value={workItemId} onChange={(event) => setWorkItemId(event.target.value)} className="w-full rounded-lg border border-border-light bg-white px-3 py-2 transition-colors focus:border-cta">
              <option value="">Project-level request</option>
              {taskOptions.map((item) => <option key={item.id} value={item.id}>{"- ".repeat(item.level || 0)}{item.title}</option>)}
            </select>
          </label>
          <label className="block" htmlFor={contextId}>
            <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Request note</span>
            <input id={contextId} value={context} onChange={(event) => setContext(event.target.value)} placeholder={workItemId ? "Uses linked work title" : "Quick update"} className="w-full rounded-lg border border-border-light bg-white px-3 py-2 transition-colors focus:border-cta" />
          </label>
        </div>
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
          <label className="block" htmlFor={photoCountId}>
            <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Number of photos</span>
            <input
              id={photoCountId}
              type="number"
              min={1}
              step={1}
              value={numberOfPhotos}
              onChange={(event) => setNumberOfPhotos(Math.max(1, Number(event.target.value) || 1))}
              className="w-full rounded-lg border border-border-light bg-white px-3 py-2 transition-colors focus:border-cta"
            />
            <span className="mt-1 block text-xs text-text-muted">Field team must upload at least this many photos.</span>
          </label>
          <label className="block" htmlFor={floorId}>
            <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Floor (optional)</span>
            <input
              id={floorId}
              value={floor}
              onChange={(event) => setFloor(event.target.value)}
              placeholder="e.g. Ground, L2, Tower A - 3F"
              className="w-full rounded-lg border border-border-light bg-white px-3 py-2 transition-colors focus:border-cta"
            />
          </label>
        </div>

        <div className="grid grid-cols-1 gap-4 sm:grid-cols-1">
          <div>
            <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Timer</span>
            <div className="flex flex-wrap items-center gap-2">
              <div className="inline-flex overflow-hidden rounded-lg border border-border-light bg-white">
                <select id={durationId} value={normalizedDurationAmount} onChange={(event) => setDurationAmount(Number(event.target.value))} className="min-w-0 border-0 bg-transparent px-3 py-2 text-sm font-semibold focus:outline-none">
                  {(DURATION_AMOUNTS[durationUnit] || DURATION_AMOUNTS.sec).map((amount) => <option key={amount} value={amount}>{amount}</option>)}
                </select>
                <select id={durationUnitId} value={durationUnit} onChange={(event) => updateDurationUnit(event.target.value)} className="border-0 border-l border-border-light bg-transparent px-2 py-2 text-xs font-bold focus:outline-none">
                  <option value="sec">sec</option>
                  <option value="min">min</option>
                </select>
              </div>
              {DURATION_PRESETS.map((preset) => {
                const active = durationSec === preset.seconds;
                return (
                  <button
                    key={preset.seconds}
                    type="button"
                    onClick={() => setDurationPreset(preset.seconds)}
                    title={`Set timer to ${formatDurationLabel(preset.seconds)}`}
                    className={`rounded-full border px-3 py-1 text-xs font-bold transition-colors ${active ? "border-cta bg-cta/10 text-cta" : "border-border-light text-text-muted hover:border-cta/40"}`}
                  >
                    {preset.label}
                  </button>
                );
              })}
            </div>
          </div>
        </div>

        <div>
          <span className="mb-2 block text-xs font-bold uppercase tracking-wider text-text-muted">Target roles</span>
          <div className="flex flex-wrap gap-2">
            {window.ConstructProData.roles.map((role) => (
              <button
                key={role}
                type="button"
                onClick={() => toggleRole(role)}
                className={`rounded-full border px-3 py-1 text-xs font-bold transition-colors ${roles.includes(role) ? "border-cta bg-cta/10 text-cta" : "border-border-light text-text-muted hover:border-cta/40"}`}
              >
                {role}
              </button>
            ))}
          </div>
        </div>

        <Button className="w-full sm:w-auto" onClick={submit} disabled={!roles.length}>
          <Icons.alert /> Send Request
        </Button>
      </div>
    </Card>
  );
}

function AlertTimeline({ alert, responses = [] }) {
  const statusTone = alert.status === "approved" ? "success" : alert.status === "escalated" ? "danger" : alert.status === "submitted" ? "info" : "warning";
  const [timelineOpen, setTimelineOpen] = React.useState(false);
  const timeline = alert.timeline || [];
  const latestEvent = timeline[timeline.length - 1];
  const timelineId = `timeline-${alert.id}`;
  return (
    <Card className="mb-3">
      <div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
        <div>
          <div className="mb-2 flex flex-wrap items-center gap-2">
            <h3 className="font-bold text-text-primary">{alert.title}</h3>
            <Badge tone={statusTone}>{alert.status}</Badge>
            <CountdownBadge remainingSec={alert.remainingSec} durationSec={alert.durationSec} />
          </div>
          <p className="text-sm text-text-muted">{alert.context}</p>
          {alert.workItemId && <p className="mt-1 text-xs font-semibold text-primary">Linked to work item {alert.workItemId}</p>}
          <div className="mt-1 flex flex-wrap items-center gap-2">
            <Badge tone="neutral">Min photos: {Math.max(1, Number(alert.numberOfPhotos || 1))}</Badge>
            {alert.floor && <Badge tone="info">Floor: {alert.floor}</Badge>}
          </div>
          <p className="mt-1 text-xs text-text-muted">Targets: {(alert.roles || []).join(", ") || "None"}</p>
        </div>
        {alert.status === "submitted" && <Button onClick={() => window.ConstructProData.approveAlert(alert.id)}><Icons.check /> Approve</Button>}
      </div>
      <div className="mt-4 grid grid-cols-1 gap-3 lg:grid-cols-2">
        <div>
          <div className="mb-2 flex items-center justify-between gap-3">
            <div>
              <p className="text-xs font-bold uppercase tracking-wider text-text-muted">Timeline</p>
              <p className="mt-1 text-xs text-text-muted">
                {timeline.length ? `${timeline.length} events · latest ${latestEvent.status} by ${latestEvent.by} · ${window.ConstructProData.formatTime(latestEvent.at)}` : "No events yet"}
              </p>
            </div>
            <button
              type="button"
              aria-expanded={timelineOpen}
              aria-controls={timelineId}
              onClick={() => setTimelineOpen((open) => !open)}
              className="rounded-full border border-border-light px-3 py-1 text-xs font-bold text-primary transition-colors hover:bg-primary-fixed"
            >
              {timelineOpen ? "Hide" : "Show"}
            </button>
          </div>
          <div id={timelineId} className={timelineOpen ? "space-y-2" : "hidden"}>
            {timeline.map((event) => (
              <div key={event.id} className="flex gap-2 text-sm">
                <span className="mt-1 h-2 w-2 rounded-full bg-cta"></span>
                <span><b>{event.status}</b> <span className="text-text-muted">by {event.by} · {window.ConstructProData.formatTime(event.at)}</span></span>
              </div>
            ))}
          </div>
        </div>
        <div>
          <p className="mb-2 text-xs font-bold uppercase tracking-wider text-text-muted">Field responses</p>
          <div className="space-y-2">
            {responses.map((response) => (
              <div key={response.role} className="rounded-lg bg-gray-50 px-3 py-2">
                <div className="flex items-center justify-between gap-3">
                  <span className="text-sm font-semibold">{response.role}</span>
                  <Badge tone={response.status === "submitted" || response.status === "approved" ? "success" : response.status === "delivered" ? "info" : response.status === "expired" ? "danger" : "warning"}>{response.status}</Badge>
                </div>
                {response.photo && <AdminPhotoPreview photo={response.photo} note={response.note} />}
              </div>
            ))}
          </div>
        </div>
      </div>
    </Card>
  );
}

function AdminPhotoPreview({ photo, note }) {
  return (
    <div className="mt-2 grid grid-cols-[92px_1fr] gap-3 rounded-xl border border-border-light bg-white p-2">
      <div className="relative h-20 overflow-hidden rounded-lg" style={{ background: photo.gradient }}>
        <div className="absolute inset-x-2 bottom-2 rounded-md bg-white/85 px-2 py-1 text-[10px] font-bold text-slate-800">{photo.title}</div>
      </div>
      <div className="min-w-0">
        <p className="text-sm font-bold text-text-primary">{photo.title}</p>
        <p className="mt-1 text-xs text-text-muted">{photo.caption}</p>
        {note && <p className="mt-2 line-clamp-2 text-xs text-text-muted">Note: {note}</p>}
      </div>
    </div>
  );
}

function InputField({ id, label, value, onChange, type = "text", required = false, placeholder = "" }) {
  return (
    <label htmlFor={id}>
      <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">{label}</span>
      <input id={id} type={type} inputMode={type === "number" ? "numeric" : undefined} required={required} value={value} placeholder={placeholder} onChange={(event) => onChange(event.target.value)} className="w-full rounded-lg border border-border-light px-3 py-2 transition-colors focus:border-cta" />
    </label>
  );
}

function AlertPanel({ snapshot }) {
  const alerts = (snapshot.alerts || []).map((alert) => ({
    ...alert,
    remainingSec: Math.max(0, Math.ceil((alert.expiresAtMs - Date.now()) / 1000))
  }));

  return (
    <div>
      <SectionTitle title="Live Alert Requests" subtitle="Track field requests, role responses, deadlines, and approval status." />
      {alerts.length ? alerts.map((alert) => {
        const responses = (alert.roles || []).map((role) => window.ConstructProData.getRoleResponse({ alertRunId: alert.id, role })).filter(Boolean);
        return <AlertTimeline key={alert.id} alert={alert} responses={responses} />;
      }) : <Card><p className="text-sm text-text-muted">No alert requests yet. Send one to collect field updates from the team.</p></Card>}
    </div>
  );
}

const BREAKPOINTS = { phone: 767, tablet: 1023, desktop: 1279 };

function useMediaQuery(query) {
  const [matches, setMatches] = useStatePrimitive(() => typeof window !== "undefined" && window.matchMedia(query).matches);
  useEffectPrimitive(() => {
    const media = window.matchMedia(query);
    const onChange = () => setMatches(media.matches);
    onChange();
    media.addEventListener("change", onChange);
    return () => media.removeEventListener("change", onChange);
  }, [query]);
  return matches;
}

function useIsPhone() {
  return useMediaQuery(`(max-width: ${BREAKPOINTS.phone}px)`);
}

function useIsCompact() {
  return useMediaQuery(`(max-width: ${BREAKPOINTS.tablet}px)`);
}

function MobileSheet({ open, onClose, title, subtitle, children, footer }) {
  useEffectPrimitive(() => {
    if (!open) return undefined;
    const previousOverflow = document.body.style.overflow;
    document.body.classList.add("constructpro-sheet-open");
    document.body.style.overflow = "hidden";
    const onKeyDown = (event) => {
      if (event.key === "Escape") onClose();
    };
    window.addEventListener("keydown", onKeyDown);
    return () => {
      document.body.classList.remove("constructpro-sheet-open");
      document.body.style.overflow = previousOverflow;
      window.removeEventListener("keydown", onKeyDown);
    };
  }, [open, onClose]);

  if (!open) return null;

  const sheet = (
    <div className="constructpro-mobile-sheet-root" role="presentation">
      <button type="button" className="constructpro-mobile-sheet-backdrop" aria-label="Close dialog" onClick={onClose} />
      <section
        role="dialog"
        aria-modal="true"
        aria-labelledby="mobile-sheet-title"
        className="constructpro-mobile-sheet-panel"
        onClick={(event) => event.stopPropagation()}
      >
        <div className="constructpro-mobile-sheet-handle" aria-hidden="true" />
        <div className="constructpro-mobile-sheet-header m3-sheet-header">
          <div className="min-w-0 flex-1 pr-2">
            <h2 id="mobile-sheet-title" className="m3-sheet-header__title">{title}</h2>
            {subtitle && <p className="m3-sheet-header__subtitle line-clamp-2">{subtitle}</p>}
          </div>
          <button type="button" onClick={onClose} aria-label="Close" className="m3-icon-button m3-icon-button--standard constructpro-mobile-sheet-close">
            <Icons.close size={24} />
          </button>
        </div>
        <div className="constructpro-mobile-sheet-body">{children}</div>
        {footer && <div className="constructpro-mobile-sheet-footer">{footer}</div>}
      </section>
    </div>
  );

  return ReactDOM.createPortal(sheet, document.body);
}

Object.assign(window, { BREAKPOINTS, useConstructProSnapshot, useMediaQuery, useIsPhone, useIsCompact, MobileSheet, Card, Button, Badge, CountdownBadge, StatCard, PageHeader, ProgressBar, SectionTitle, RowStatusPicker, RowActionMenu, RowAddControl, InputField, AlertLauncher, AlertPanel, AlertTimeline });
