<!-- #region MaintenanceEventsTable [C:3] [TYPE Component] [SEMANTICS maintenance, table, events, active, completed, remove, expandable] -->
<!-- @ingroup Components -->
<!-- @BRIEF Active/completed maintenance events table with removal actions and expandable dashboard list per event. -->
<!-- @LAYER Component -->
<!-- @RELATION DEPENDS_ON -> [MaintenanceStore] -->
<!-- @RELATION DEPENDS_ON -> [MaintenanceApi] -->
<!-- @INVARIANT expandedEventIds is always a subset of active event IDs. -->
<!-- @INVARIANT Only one ConfirmDialog can be open at a time. -->
<!-- @UX_STATE idle -> Table rendered with events. -->
<!-- @UX_STATE removing -> One row shows spinner on its Remove button, other buttons disabled. -->
<!-- @UX_STATE removing_all -> All buttons disabled, "Remove All" shows spinner. -->
<!-- @UX_STATE empty -> No active events: dashed border placeholder. -->
<!-- @UX_STATE loading -> Skeleton rows displayed. -->
<!-- @UX_STATE error -> Toast shown on removal failure. -->
<!-- @UX_STATE expanded -> Dashboard list visible in sub-row for clicked event. -->
<!-- @UX_TRANSITION idle -> expanded: user clicks dashboard count. -->
<!-- @UX_TRANSITION expanded -> idle: user clicks again or event removed. -->
<!-- @UX_TRANSITION idle -> removing: user confirms remove dialog. -->
<!-- @UX_TRANSITION removing -> idle: remove completes or fails. -->
<!-- @UX_FEEDBACK Toast success/error on removal. -->
<!-- @UX_RECOVERY Retry via individual or bulk remove. -->
<!-- @UX_REACTIVITY Props -> $props(), LocalState -> $state(removingEventId, isRemovingAll, expandedEventIds) -->
<script lang="ts">
  import { maintenanceStore } from "$lib/stores/maintenance.svelte.js";
  import Button from "$lib/ui/Button.svelte";
  import { EmptyState, ConfirmDialog } from "$lib/ui";
  import { t } from "$lib/i18n/index.svelte.js";

  // ── Types ─────────────────────────────────────────────────────
  interface MaintenanceDashboard {
    id: number;
    title: string;
  }

  interface MaintenanceEvent {
    id: string;
    tables?: string[];
    affected_count?: number;
    dashboards?: MaintenanceDashboard[];
    start_time?: string;
    end_time?: string;
    status?: string;
    [k: string]: unknown;
  }

  // ── State ─────────────────────────────────────────────────────
  let removingEventId = $state<string | null>(null);
  let isRemovingAll = $state(false);
  let showRemoveConfirm = $state(false);
  let showRemoveAllConfirm = $state(false);
  let removeTarget = $state<MaintenanceEvent | null>(null);

  // ── Expandable rows ─────────────────────────────────────────
  let expandedEventIds = $state<Set<string>>(new Set());

  // ── Constants ────────────────────────────────────────────────
  const MAX_ID_DISPLAY_LENGTH = 16;

  let isLoading = $derived(maintenanceStore.isLoading);
  let activeEvents = $derived(maintenanceStore.activeEvents as MaintenanceEvent[]);
  let completedEvents = $derived(maintenanceStore.completedEvents as MaintenanceEvent[]);

  // #region toggleExpanded [C:2] [TYPE Function]
  // @BRIEF Toggle expanded state for an event's dashboard list.
  // @PRE eventId is a non-empty string.
  // @POST expandedEventIds contains or does not contain eventId.
  function toggleExpanded(eventId: string): void {
    const next = new Set(expandedEventIds);
    if (next.has(eventId)) {
      next.delete(eventId);
    } else {
      next.add(eventId);
    }
    expandedEventIds = next;
  }
  // #endregion toggleExpanded

  // #region statusBadgeClass [C:2] [TYPE Function]
  // @BRIEF Map event status to Tailwind badge classes.
  // @PRE status is a string or null/undefined.
  // @POST Returns a valid Tailwind class string.
  function statusBadgeClass(status: string | undefined | null): string {
    switch ((status || "").toLowerCase()) {
      case "active":
      case "applying":
      case "pending":
        return "bg-success-light text-success";
      case "completed":
        return "bg-primary-light text-primary";
      case "failed":
      case "partial":
        return "bg-destructive-light text-destructive";
      case "ending":
        return "bg-warning-light text-warning";
      default:
        return "bg-surface-muted text-text";
    }
  }
  // #endregion statusBadgeClass

  // #region formatTime [C:2] [TYPE Function]
  // @BRIEF Format an ISO timestamp string for display.
  // @PRE ts is an ISO-8601 string or null/undefined.
  // @POST Returns localized time string or "-" for missing input.
  function formatTime(ts: string | undefined | null): string {
    if (!ts) return "-";
    try {
      const d = new Date(ts);
      if (isNaN(d.getTime())) return ts;
      return d.toLocaleString();
    } catch {
      return ts;
    }
  }
  // #endregion formatTime

  // #region truncateId [C:1] [TYPE Function]
  // @BRIEF Truncate long ID for display with ellipsis.
  function truncateId(id: string | undefined): string {
    if (!id) return "-";
    return id.length > MAX_ID_DISPLAY_LENGTH ? id.slice(0, MAX_ID_DISPLAY_LENGTH) + "…" : id;
  }
  // #endregion truncateId

  // #region promptRemove [C:2] [TYPE Function]
  // @BRIEF Open confirmation dialog for single event removal.
  function promptRemove(event: MaintenanceEvent): void {
    removeTarget = event;
    showRemoveConfirm = true;
  }
  // #endregion promptRemove

  // #region onConfirmRemove [C:2] [TYPE Function]
  // @BRIEF Handle confirmed removal of a single event. Cleans up expanded state.
  // @POST expandedEventIds no longer contains the removed event ID.
  async function onConfirmRemove(): Promise<void> {
    const event = removeTarget;
    if (!event) return;
    removeTarget = null;

    // @INVARIANT: cleanup expanded state before removal
    expandedEventIds.delete(event.id);
    expandedEventIds = new Set(expandedEventIds);

    removingEventId = event.id;
    try {
      await maintenanceStore.endEvent(event.id);
    } catch {
      // Toast is handled by store
    } finally {
      removingEventId = null;
    }
  }
  // #endregion onConfirmRemove

  // #region promptRemoveAll [C:1] [TYPE Function]
  // @BRIEF Open confirmation dialog for bulk removal.
  function promptRemoveAll(): void {
    showRemoveAllConfirm = true;
  }
  // #endregion promptRemoveAll

  // #region onConfirmRemoveAll [C:2] [TYPE Function]
  // @BRIEF Handle confirmed removal of all events. Clears all expanded state.
  // @POST expandedEventIds is empty after execution.
  async function onConfirmRemoveAll(): Promise<void> {
    isRemovingAll = true;
    expandedEventIds = new Set();
    try {
      await maintenanceStore.endAllEvents();
    } catch {
      // Toast handled by store
    } finally {
      isRemovingAll = false;
    }
  }
  // #endregion onConfirmRemoveAll

  // #region joinTables [C:1] [TYPE Function]
  // @BRIEF Join table names with comma, return "-" if empty.
  function joinTables(tables: string[] | undefined): string {
    return (tables || []).join(", ") || "-";
  }
  // #endregion joinTables
</script>

<div class="space-y-6">
  <!-- Active Events -->
  <div>
    <div class="flex items-center justify-between mb-3">
      <h3 class="text-lg font-semibold text-text">
        {$t.maintenance?.active_events || "Active Events"}
      </h3>

      {#if activeEvents.length > 0}
        <Button
          variant="danger"
          size="sm"
          onclick={promptRemoveAll}
          isLoading={isRemovingAll}
          disabled={isRemovingAll}
        >
          {$t.maintenance?.remove_all || "Remove All"}
        </Button>
      {/if}
    </div>

    {#if isLoading && activeEvents.length === 0}
      <!-- Skeleton -->
      <div class="border border-border rounded-lg overflow-hidden">
        <table class="min-w-full divide-y divide-border" aria-label="Loading maintenance events">
          <thead class="bg-surface-muted">
            <tr>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">ID</th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Tables</th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Dashboards</th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Start</th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">End</th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Status</th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Actions</th>
            </tr>
          </thead>
          <tbody class="bg-surface-card divide-y divide-border">
            {#each [1, 2, 3] as _, i (i)}
              <tr>
                <td class="px-4 py-3"><div class="animate-pulse bg-surface-muted h-4 w-24 rounded"></div></td>
                <td class="px-4 py-3"><div class="animate-pulse bg-surface-muted h-4 w-32 rounded"></div></td>
                <td class="px-4 py-3"><div class="animate-pulse bg-surface-muted h-4 w-16 rounded"></div></td>
                <td class="px-4 py-3"><div class="animate-pulse bg-surface-muted h-4 w-28 rounded"></div></td>
                <td class="px-4 py-3"><div class="animate-pulse bg-surface-muted h-4 w-28 rounded"></div></td>
                <td class="px-4 py-3"><div class="animate-pulse bg-surface-muted h-6 w-16 rounded-full"></div></td>
                <td class="px-4 py-3"><div class="animate-pulse bg-surface-muted h-8 w-24 rounded"></div></td>
              </tr>
            {/each}
          </tbody>
        </table>
      </div>
    {:else if activeEvents.length === 0}
      <!-- Empty state -->
      <EmptyState title={$t.maintenance?.no_active_events || "No active maintenance events"} />
    {:else}
      <!-- Events table -->
      <div class="border border-border rounded-lg overflow-hidden">
        <table class="min-w-full divide-y divide-border" aria-label={$t.maintenance?.active_events || "Active Events"}>
          <thead class="bg-surface-muted">
            <tr>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
                {$t.maintenance?.event_id || "Event ID"}
              </th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
                {$t.maintenance?.tables || "Tables"}
              </th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
                {$t.maintenance?.affected_dashboards || "Dashboards"}
              </th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
                {$t.maintenance?.start_time || "Start Time"}
              </th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
                {$t.maintenance?.end_time || "End Time"}
              </th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
                {$t.maintenance?.status || "Status"}
              </th>
              <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
                {$t.maintenance?.actions || "Actions"}
              </th>
            </tr>
          </thead>
          <tbody class="bg-surface-card divide-y divide-border">
            {#each activeEvents as event (event.id)}
              {@const isExpanded = expandedEventIds.has(event.id)}
              {@const hasDashboards = (event.dashboards?.length ?? 0) > 0}
              <tr class="hover:bg-surface-muted" aria-expanded={isExpanded}>
                <td class="px-4 py-3 text-sm font-mono text-text" title={event.id}>
                  {truncateId(event.id)}
                </td>
                <td class="px-4 py-3 text-sm text-text-muted max-w-[200px] truncate" title={joinTables(event.tables)}>
                  {joinTables(event.tables)}
                </td>
                <td class="px-4 py-3 text-sm">
                  {#if hasDashboards}
                    <button
                      type="button"
                      onclick={() => toggleExpanded(event.id)}
                      aria-expanded={isExpanded}
                      aria-label={isExpanded
                        ? ($t.maintenance?.collapse_dashboards || "Hide dashboards")
                        : ($t.maintenance?.expand_dashboards || "Show {count} dashboards").replace("{count}", String(event.affected_count ?? 0))
                      }
                      class="inline-flex items-center gap-1 text-text hover:text-primary transition-colors cursor-pointer"
                    >
                      <svg
                        class="w-3.5 h-3.5 transition-transform {isExpanded ? 'rotate-90' : ''}"
                        fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"
                        aria-hidden="true"
                      >
                        <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
                      </svg>
                      <span class="font-medium">{event.affected_count ?? "-"}</span>
                    </button>
                  {:else}
                    <span class="text-text">{event.affected_count ?? "-"}</span>
                  {/if}
                </td>
                <td class="px-4 py-3 text-sm text-text-muted">
                  {formatTime(event.start_time)}
                </td>
                <td class="px-4 py-3 text-sm text-text-muted">
                  {formatTime(event.end_time)}
                </td>
                <td class="px-4 py-3">
                  <span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {statusBadgeClass(event.status)}">
                    {event.status}
                  </span>
                </td>
                <td class="px-4 py-3">
                  <Button
                    variant="danger"
                    size="sm"
                    onclick={() => promptRemove(event)}
                    isLoading={removingEventId === event.id}
                    disabled={isRemovingAll || removingEventId !== null}
                  >
                    {$t.maintenance?.remove_banners || "Remove"}
                  </Button>
                </td>
              </tr>
              {#if isExpanded && hasDashboards}
                <tr role="row" aria-label="Affected dashboards list">
                  <td colspan="7" class="px-4 py-2 bg-surface-muted">
                    <div class="pl-6">
                      <p class="text-xs font-medium text-text-muted mb-1">
                        {$t.maintenance?.affected_dashboards_list || "Affected dashboards"}:
                      </p>
                      <ul class="space-y-0.5" role="list">
                        {#each event.dashboards ?? [] as dash (dash.id)}
                          <li class="text-xs text-text flex items-center gap-1.5">
                            <span class="inline-block w-1.5 h-1.5 rounded-full bg-warning-DEFAULT flex-shrink-0" aria-hidden="true"></span>
                            <span class="truncate">{dash.title}</span>
                            <span class="text-text-subtle flex-shrink-0">#{dash.id}</span>
                          </li>
                        {/each}
                      </ul>
                    </div>
                  </td>
                </tr>
              {/if}
            {/each}
          </tbody>
        </table>
      </div>
    {/if}
  </div>

  <!-- Completed Events (collapsible) -->
  {#if completedEvents.length > 0}
    <details class="rounded-lg border border-border bg-surface-card">
      <summary class="cursor-pointer px-4 py-3 text-sm font-semibold text-text hover:bg-surface-muted rounded-lg">
        {$t.maintenance?.completed_events || "Completed Events"} ({completedEvents.length})
      </summary>

      <div class="px-4 pb-4">
        <div class="border border-border rounded-lg overflow-hidden">
          <table class="min-w-full divide-y divide-border" aria-label={$t.maintenance?.completed_events || "Completed Events"}>
            <thead class="bg-surface-muted">
              <tr>
                <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">ID</th>
                <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Tables</th>
                <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Dashboards</th>
                <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Start</th>
                <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">End</th>
                <th class="px-4 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">Status</th>
              </tr>
            </thead>
            <tbody class="bg-surface-card divide-y divide-border">
              {#each completedEvents as event (event.id)}
                <tr class="hover:bg-surface-muted">
                  <td class="px-4 py-3 text-sm font-mono text-text" title={event.id}>
                    {truncateId(event.id)}
                  </td>
                  <td class="px-4 py-3 text-sm text-text-muted max-w-[200px] truncate" title={joinTables(event.tables)}>
                    {joinTables(event.tables)}
                  </td>
                  <td class="px-4 py-3 text-sm text-text">
                    {event.affected_count ?? "-"}
                  </td>
                  <td class="px-4 py-3 text-sm text-text-muted">
                    {formatTime(event.start_time)}
                  </td>
                  <td class="px-4 py-3 text-sm text-text-muted">
                    {formatTime(event.end_time)}
                  </td>
                  <td class="px-4 py-3">
                    <span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {statusBadgeClass(event.status)}">
                      {event.status}
                    </span>
                  </td>
                </tr>
              {/each}
            </tbody>
          </table>
        </div>
      </div>
    </details>
  {/if}
</div>

<ConfirmDialog
  bind:show={showRemoveConfirm}
  title={$t.maintenance?.remove_event_title || "Remove event?"}
  message={$t.maintenance?.remove_event_confirm || "Remove banners for this event?"}
  variant="destructive"
  confirmLabel={$t.maintenance?.remove_banners || "Remove"}
  cancelLabel={$t.common?.cancel || "Cancel"}
  onConfirm={onConfirmRemove}
  onCancel={() => {}}
/>

<ConfirmDialog
  bind:show={showRemoveAllConfirm}
  title={$t.maintenance?.remove_all_title || "Remove all events?"}
  message={$t.maintenance?.remove_all_confirm || "Remove all active maintenance banners?"}
  variant="destructive"
  confirmLabel={$t.maintenance?.remove_all || "Remove All"}
  cancelLabel={$t.common?.cancel || "Cancel"}
  onConfirm={onConfirmRemoveAll}
  onCancel={() => {}}
/>
<!-- #endregion MaintenanceEventsTable -->
