<script lang="ts">
  import { onMount } from "svelte";
  import type { Snippet } from "svelte";
  import { fade, fly } from "svelte/transition";
  import {
    GOOGLE_CALENDAR_SYNC_WEEKS_MAX,
    GOOGLE_CALENDAR_SYNC_WEEKS_MIN,
  } from "@/config/googleCalendar";
  import { maybeRunDueWeeklySync } from "@/seqta/utils/googleCalendar/calendarSyncListener";
  import { formatLessonSyncResultMessage } from "@/seqta/utils/calendarSync/lessonSyncShared";
  import {
    deleteSyncedEventsFromGoogleCalendar,
    deleteSyncedEventsFromOutlookCalendar,
  } from "@/seqta/utils/calendarSync/syncEngine";
  import {
    connectCalendarProvider,
    disconnectCalendarProvider,
    fetchCalendarStatuses,
    getCalendarAccessToken,
    runGoogleCalendarSync,
    runOutlookCalendarSync,
    updateGoogleSyncSettings,
  } from "@/seqta/utils/calendarSync/syncRunner";
  import type {
    GoogleCalendarStatus,
    GoogleCalendarSyncProgress,
  } from "@/seqta/utils/googleCalendar/types";
  import type { OutlookCalendarStatus } from "@/seqta/utils/calendarSync/providerStorage";
  import OutlookCalendarIcon from "./OutlookCalendarIcon.svelte";
  import { settingsState } from "@/seqta/utils/listeners/SettingsState";
  import {
    formatLastSync,
    isCalendarSyncModalTarget,
    portalToBody,
    providerLabel as calendarProviderLabel,
    syncCalendarSyncTheme,
    syncProgressPercent,
    type CalendarProvider,
  } from "./calendarSyncUi";

  type ProviderStatus = { configured: boolean; connected: boolean; lastSyncAt?: number };

  type BusyPhase = "connect" | "sync" | "delete" | "disconnect" | null;
  type BusyState = { provider: CalendarProvider; phase: BusyPhase } | null;
  function setProviderStatus(provider: CalendarProvider, patch: Partial<ProviderStatus>) {
    if (provider === "google") googleStatus = { ...googleStatus, ...patch };
    else outlookStatus = { ...outlookStatus, ...patch };
  }

  function providerStatus(provider: CalendarProvider): ProviderStatus {
    return provider === "google" ? googleStatus : outlookStatus;
  }

  let googleStatus = $state<GoogleCalendarStatus>({ configured: true, connected: false });
  let outlookStatus = $state<OutlookCalendarStatus>({ configured: true, connected: false });
  let busy = $state<BusyState>(null);
  let menuOpen = $state(false);
  let modalProvider = $state<CalendarProvider | null>(null);
  let showDisconnect = $state(false);
  let showDeleteEvents = $state(false);
  let disconnectOverlay = $state(false);
  let toast = $state<{ message: string; error: boolean } | null>(null);
  let syncProgress = $state<GoogleCalendarSyncProgress | null>(null);
  let syncWeeksAhead = $state(12);
  let autoSyncWeekly = $state(true);

  let rootEl = $state<HTMLDivElement | null>(null);
  let triggerEl = $state<HTMLButtonElement | null>(null);
  let menuEl = $state<HTMLDivElement | null>(null);
  let modalEl = $state<HTMLDivElement | null>(null);
  let overlayEl = $state<HTMLDivElement | null>(null);
  let menuStyle = $state("");
  let toastTimer: ReturnType<typeof setTimeout> | null = null;

  const isBusy = $derived(busy !== null);
  const anyConnected = $derived(googleStatus.connected || outlookStatus.connected);
  const modalOpen = $derived(showDisconnect || showDeleteEvents);
  const modalBusy = $derived(showDisconnect ? busy?.phase === "disconnect" : busy?.phase === "delete");
  const providerLabel = $derived(calendarProviderLabel(modalProvider ?? "google"));
  const showTriggerProgress = $derived(
    isBusy &&
      (busy?.phase === "sync" ||
        busy?.phase === "delete" ||
        busy?.phase === "disconnect" ||
        busy?.phase === "connect" ||
        (syncProgress !== null && syncProgress.phase !== "done")),
  );
  const triggerProgressPercent = $derived.by(() => {
    if (!showTriggerProgress) return 0;
    if (syncProgress && syncProgress.phase !== "done") {
      return syncProgressPercent(syncProgress);
    }
    return 10;
  });
  const triggerStatusText = $derived.by(() => {
    if (!showTriggerProgress) return "Sync with Calendar";
    const verb =
      busy?.phase === "delete"
        ? "Deleting"
        : busy?.phase === "disconnect"
          ? "Disconnecting"
          : "Syncing";
    if (syncProgress?.total) return `${verb} ${triggerProgressPercent}%`;
    return `${verb}…`;
  });
  const triggerAriaLabel = $derived.by(() => {
    if (!showTriggerProgress) {
      return anyConnected ? "Calendar sync options" : "Sync with Calendar";
    }
    if (syncProgress?.total) {
      const verb =
        busy?.phase === "delete"
          ? "Deleting"
          : busy?.phase === "disconnect"
            ? "Disconnecting"
            : "Syncing";
      return `Calendar ${verb.toLowerCase()} in progress, ${triggerProgressPercent} percent complete`;
    }
    if (busy?.phase === "delete") return "Calendar deletion in progress";
    if (busy?.phase === "disconnect") return "Calendar disconnect in progress";
    return "Calendar sync in progress";
  });
  const accent = "var(--bsplus-cal-accent, var(--better-main, #3b82f6))";

  const overlayProgressPercent = $derived(syncProgressPercent(syncProgress));
  const overlayStatusText = $derived.by(() => {
    if (busy?.phase === "disconnect") return "Disconnecting account…";
    if (syncProgress?.message) return syncProgress.message;
    return "Removing synced classes…";
  });

  function providerPhase(provider: CalendarProvider): BusyPhase {
    return busy?.provider === provider ? busy.phase : null;
  }

  function providerStatusText(status: ProviderStatus, notConfigured: string): string {
    if (!status.configured) return notConfigured;
    if (!status.connected) return "Not connected";
    const lastSync = formatLastSync(status.lastSyncAt);
    return lastSync ? `Connected · ${lastSync}` : "Connected";
  }

  function closeModal() {
    if (showDisconnect && busy?.phase !== "disconnect") showDisconnect = false;
    if (showDeleteEvents && busy?.phase !== "delete") showDeleteEvents = false;
  }

  function showToastMessage(message: string, isError = false) {
    toast = { message, error: isError };
    if (toastTimer) clearTimeout(toastTimer);
    toastTimer = setTimeout(() => {
      toast = null;
    }, 4500);
  }

  async function refreshStatus() {
    const { google, outlook } = await fetchCalendarStatuses();
    googleStatus = google;
    outlookStatus = outlook;
    syncWeeksAhead = google.syncWeeksAhead ?? 12;
    autoSyncWeekly = google.autoSyncWeekly !== false;
  }

  function handleSyncProgress(progress: GoogleCalendarSyncProgress) {
    syncProgress = progress;
  }

  async function saveSyncSettings(patch: {
    syncWeeksAhead?: number;
    autoSyncWeekly?: boolean;
  }) {
    const result = await updateGoogleSyncSettings(patch);
    if (result.syncWeeksAhead != null) syncWeeksAhead = result.syncWeeksAhead;
    if (result.autoSyncWeekly != null) autoSyncWeekly = result.autoSyncWeekly;
    googleStatus = { ...googleStatus, ...result };
  }

  async function performSync(
    provider: CalendarProvider,
    mode: "full" | "incremental" = "full",
  ): Promise<boolean> {
    const run = provider === "google" ? runGoogleCalendarSync : runOutlookCalendarSync;
    const result = await run({ mode, onProgress: handleSyncProgress });
    syncProgress = null;

    if (!result.success) {
      showToastMessage(result.error ?? "Calendar sync failed.", true);
      return false;
    }

    setProviderStatus(provider, {
      connected: true,
      lastSyncAt: result.lastSyncAt ?? providerStatus(provider).lastSyncAt,
    });

    showToastMessage(
      formatLessonSyncResultMessage(result, `${calendarProviderLabel(provider)} Calendar`),
    );
    return true;
  }

  async function connectProvider(provider: CalendarProvider) {
    const status = providerStatus(provider);
    if (!status.configured || isBusy) return;
    menuOpen = false;
    busy = { provider, phase: "connect" };
    try {
      const result = await connectCalendarProvider(provider);
      if (!result.success) {
        showToastMessage(
          result.error ?? `Could not connect to ${calendarProviderLabel(provider)} Calendar.`,
          true,
        );
        return;
      }
      setProviderStatus(provider, { connected: true });
      busy = { provider, phase: "sync" };
      await performSync(provider);
    } catch (err) {
      showToastMessage(err instanceof Error ? err.message : "Could not connect.", true);
    } finally {
      syncProgress = null;
      busy = null;
    }
  }
  async function syncProvider(provider: CalendarProvider) {
    const status = providerStatus(provider);
    if (!status.configured || isBusy) return;
    if (!status.connected) {
      await connectProvider(provider);
      return;
    }

    busy = { provider, phase: "sync" };
    menuOpen = false;
    try {
      await performSync(provider);
    } catch (err) {
      showToastMessage(err instanceof Error ? err.message : "Calendar sync failed.", true);
    } finally {
      syncProgress = null;
      busy = null;
    }
  }

  async function deleteProviderEvents(provider: CalendarProvider) {
    const deleteFn =
      provider === "google"
        ? deleteSyncedEventsFromGoogleCalendar
        : deleteSyncedEventsFromOutlookCalendar;
    return deleteFn(location.origin, () => getCalendarAccessToken(provider), {
      onProgress: handleSyncProgress,
    });
  }

  async function confirmDeleteEvents() {
    if (isBusy || !modalProvider) return;
    const provider = modalProvider;
    showDeleteEvents = false;
    menuOpen = false;
    busy = { provider, phase: "delete" };
    syncProgress = {
      phase: "preparing",
      current: 0,
      total: 1,
      message: "Preparing removal…",
    };
    try {
      const result = await deleteProviderEvents(provider);

      if (!result.success) {
        showToastMessage(result.error ?? "Could not remove calendar events.", true);
        return;
      }

      const removed = result.deleted ?? 0;
      modalProvider = null;
      const label = calendarProviderLabel(provider);
      showToastMessage(
        removed === 0
          ? "No synced events to remove."
          : `Removed ${removed} event${removed === 1 ? "" : "s"} from ${label} Calendar.`,
      );
    } catch (err) {
      showToastMessage(err instanceof Error ? err.message : "Remove failed.", true);
    } finally {
      syncProgress = null;
      busy = null;
    }
  }

  async function onWeeksAheadChange(event: Event) {
    const value = Number((event.currentTarget as HTMLInputElement).value);
    if (!Number.isFinite(value)) return;
    await saveSyncSettings({ syncWeeksAhead: value });
  }

  async function onAutoSyncToggle(event: Event) {
    const checked = (event.currentTarget as HTMLInputElement).checked;
    autoSyncWeekly = checked;
    await saveSyncSettings({ autoSyncWeekly: checked });
  }

  async function disconnectProvider(
    provider: CalendarProvider,
    label: string,
    toastMessage?: string,
  ) {
    const result = await disconnectCalendarProvider(provider);
    if (!result?.success) {
      showToastMessage(`Could not disconnect ${label} Calendar.`, true);
      return false;
    }
    setProviderStatus(provider, { connected: false, lastSyncAt: undefined });
    modalProvider = null;
    if (toastMessage) showToastMessage(toastMessage);
    return true;
  }

  async function confirmDisconnect(deleteEvents: boolean) {
    if (isBusy || !modalProvider) return;
    const provider = modalProvider;
    const label = calendarProviderLabel(provider);
    menuOpen = false;

    if (deleteEvents) {
      showDisconnect = false;
      disconnectOverlay = true;
      busy = { provider, phase: "delete" };
      syncProgress = {
        phase: "preparing",
        current: 0,
        total: 1,
        message: "Preparing removal…",
      };
      try {
        const deleteResult = await deleteProviderEvents(provider);
        if (!deleteResult.success) {
          showToastMessage(deleteResult.error ?? "Could not remove calendar events.", true);
          return;
        }

        busy = { provider, phase: "disconnect" };
        syncProgress = {
          phase: "preparing",
          current: 0,
          total: 1,
          message: "Disconnecting account…",
        };

        const removed = deleteResult.deleted ?? 0;
        const toastMessage =
          removed > 0
            ? `Removed ${removed} event${removed === 1 ? "" : "s"} and disconnected from ${label} Calendar.`
            : `Disconnected from ${label} Calendar.`;
        await disconnectProvider(provider, label, toastMessage);
      } catch (err) {
        showToastMessage(err instanceof Error ? err.message : "Disconnect failed.", true);
      } finally {
        syncProgress = null;
        busy = null;
        disconnectOverlay = false;
      }
      return;
    }

    busy = { provider, phase: "disconnect" };
    try {
      const ok = await disconnectProvider(
        provider,
        label,
        `Disconnected from ${label} Calendar.`,
      );
      if (ok) showDisconnect = false;
    } catch (err) {
      showToastMessage(err instanceof Error ? err.message : "Disconnect failed.", true);
    } finally {
      busy = null;
    }
  }

  function openModal(provider: CalendarProvider, kind: "delete" | "disconnect") {
    if (isBusy) return;
    modalProvider = provider;
    menuOpen = false;
    showDisconnect = kind === "disconnect";
    showDeleteEvents = kind === "delete";
  }

  function toggleMenu() {
    menuOpen = !menuOpen;
    if (menuOpen) queueMicrotask(syncHostTheme);
  }

  function updateMenuPosition() {
    if (!triggerEl) return;
    const rect = triggerEl.getBoundingClientRect();
    menuStyle = `top:${rect.bottom + 8}px;right:${window.innerWidth - rect.right}px;`;
    syncHostTheme();
  }

  function syncHostTheme() {
    const themeHost = rootEl?.closest(".bsplus-calendar-sync-mount");
    if (themeHost instanceof HTMLElement) syncCalendarSyncTheme(themeHost);
    if (menuEl) syncCalendarSyncTheme(menuEl);
    if (modalEl) syncCalendarSyncTheme(modalEl);
    if (overlayEl) syncCalendarSyncTheme(overlayEl);
  }

  $effect(() => {
    if (menuOpen && menuEl) syncHostTheme();
  });

  $effect(() => {
    if (modalOpen && modalEl) syncHostTheme();
  });

  $effect(() => {
    if (disconnectOverlay && overlayEl) syncHostTheme();
  });

  $effect(() => {
    if (!disconnectOverlay) return;
    const onBeforeUnload = (event: BeforeUnloadEvent) => {
      event.preventDefault();
      event.returnValue = "";
    };
    window.addEventListener("beforeunload", onBeforeUnload);
    return () => window.removeEventListener("beforeunload", onBeforeUnload);
  });

  $effect(() => {
    if (!menuOpen || !triggerEl) return;
    updateMenuPosition();
    const onLayout = () => updateMenuPosition();
    window.addEventListener("resize", onLayout);
    window.addEventListener("scroll", onLayout, true);
    return () => {
      window.removeEventListener("resize", onLayout);
      window.removeEventListener("scroll", onLayout, true);
    };
  });

  onMount(() => {
    void refreshStatus().then(() => {
      void maybeRunDueWeeklySync((message, isError) => {
        showToastMessage(message, isError);
        void refreshStatus();
      });
    });

    const themeKeys = [
      "selectedColor",
      "selectedFont",
      "DarkMode",
      "adaptiveThemeColour",
      "adaptiveThemeGradient",
      "selectedTheme",
    ] as const;
    const onThemeChange = () => syncHostTheme();
    for (const key of themeKeys) {
      settingsState.register(key, onThemeChange);
    }

    const themeObserver = new MutationObserver(onThemeChange);
    themeObserver.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["style", "class"],
    });

    const onDocPointer = (event: PointerEvent) => {
      if (!menuOpen) return;
      const target = event.target as Node;
      if (rootEl?.contains(target)) return;
      if (menuEl?.contains(target)) return;
      if (isCalendarSyncModalTarget(target)) return;
      menuOpen = false;
    };

    document.addEventListener("pointerdown", onDocPointer);
    return () => {
      document.removeEventListener("pointerdown", onDocPointer);
      for (const key of themeKeys) {
        settingsState.unregister(key, onThemeChange);
      }
      themeObserver.disconnect();
      if (toastTimer) clearTimeout(toastTimer);
    };
  });
</script>

{#snippet providerPanel(
  provider: CalendarProvider,
  status: ProviderStatus,
  notConfiguredMsg: string,
  icon: Snippet,
  name: Snippet,
)}
  <div class="bsplus-cal-provider" role="none">
    <div class="bsplus-cal-provider-row">
      <span class="bsplus-cal-provider-icon" aria-hidden="true">
        {@render icon()}
      </span>
      <div class="bsplus-cal-provider-copy">
        <span class="bsplus-cal-provider-name">{@render name()}</span>
        <span class="bsplus-cal-provider-status">
          {providerStatusText(status, notConfiguredMsg)}
        </span>
      </div>
    </div>

    <div class="bsplus-cal-provider-actions">
      {#if !status.connected}
        <button
          type="button"
          class="bsplus-cal-action bsplus-cal-action--primary"
          style:--bsplus-cal-accent={accent}
          role="menuitem"
          disabled={!status.configured || isBusy}
          onclick={() => void connectProvider(provider)}
        >
          {providerPhase(provider) === "connect" ? "Connecting…" : "Connect & sync"}
        </button>
      {:else}
        <button
          type="button"
          class="bsplus-cal-action bsplus-cal-action--primary"
          style:--bsplus-cal-accent={accent}
          role="menuitem"
          disabled={isBusy}
          onclick={() => void syncProvider(provider)}
        >
          {providerPhase(provider) === "sync" ? "Updating…" : "Update calendar"}
        </button>
        <button
          type="button"
          class="bsplus-cal-action bsplus-cal-action--ghost"
          role="menuitem"
          disabled={isBusy}
          onclick={() => openModal(provider, "delete")}
        >
          {providerPhase(provider) === "delete" ? "Deleting…" : "Delete synced classes"}
        </button>
        <button
          type="button"
          class="bsplus-cal-action bsplus-cal-action--ghost"
          role="menuitem"
          disabled={isBusy}
          onclick={() => openModal(provider, "disconnect")}
        >
          Disconnect account
        </button>
      {/if}
    </div>
  </div>
{/snippet}

<div class="bsplus-cal-sync" bind:this={rootEl}>
  <button
    type="button"
    class="uiButton bsplus-cal-trigger"
    bind:this={triggerEl}
    class:bsplus-cal-trigger--open={menuOpen}
    class:bsplus-cal-trigger--busy={isBusy}
    class:bsplus-cal-trigger--progress={showTriggerProgress}
    style:--bsplus-cal-trigger-progress="{triggerProgressPercent}%"
    aria-haspopup="menu"
    aria-expanded={menuOpen}
    aria-busy={isBusy}
    aria-label={triggerAriaLabel}
    onclick={() => toggleMenu()}
  >
    <span class="bsplus-cal-trigger-fill" aria-hidden="true"></span>
    <span class="bsplus-cal-trigger-content">
      <span class="bsplus-cal-trigger-icon iconFamily" aria-hidden="true">&#xe9cd;</span>
      <span class="bsplus-cal-trigger-text">{triggerStatusText}</span>
    </span>
  </button>

  {#if menuOpen}
    <div
      class="bsplus-cal-menu"
      role="menu"
      bind:this={menuEl}
      style={menuStyle}
      use:portalToBody
      transition:fly={{ y: -6, duration: 160 }}
    >
      <div class="bsplus-cal-menu-header">
        <span class="bsplus-cal-menu-title">Calendar sync</span>
        <span class="bsplus-cal-menu-sub">Copy your SEQTA classes and appointments to Google or Outlook</span>
      </div>

      {@render providerPanel(
        "google",
        googleStatus,
        "Not available in this build",
        googleIcon,
        googleName,
      )}
      {@render providerPanel(
        "outlook",
        outlookStatus,
        "Set OUTLOOK_OAUTH_CLIENT_ID to enable",
        outlookIcon,
        outlookName,
      )}

      {#if anyConnected}
        <div class="bsplus-cal-settings" role="group" aria-label="Sync options">
          <label class="bsplus-cal-setting">
            <div class="bsplus-cal-setting-copy">
              <span class="bsplus-cal-setting-label">Weeks to sync</span>
              <span class="bsplus-cal-setting-desc">
                How many weeks of classes to add when you connect or tap Update calendar.
              </span>
            </div>
            <input
              type="number"
              class="bsplus-cal-setting-input"
              min={GOOGLE_CALENDAR_SYNC_WEEKS_MIN}
              max={GOOGLE_CALENDAR_SYNC_WEEKS_MAX}
              value={syncWeeksAhead}
              disabled={isBusy}
              onchange={(e) => void onWeeksAheadChange(e)}
            />
          </label>
          <label class="bsplus-cal-setting bsplus-cal-setting--toggle">
            <div class="bsplus-cal-setting-copy">
              <span class="bsplus-cal-setting-label">Sync new weeks automatically</span>
              <span class="bsplus-cal-setting-desc">
                Each week, add the next week of your timetable without opening this menu.
              </span>
            </div>
            <input
              type="checkbox"
              class="bsplus-cal-setting-checkbox"
              checked={autoSyncWeekly}
              disabled={isBusy}
              onchange={(e) => void onAutoSyncToggle(e)}
            />
          </label>
        </div>
      {/if}
    </div>
  {/if}

  {#if modalOpen}
    <div
      class="bsplus-cal-modal-backdrop"
      bind:this={modalEl}
      use:portalToBody
      onclick={(e) => {
        if (e.target === e.currentTarget && !modalBusy) closeModal();
      }}
      onkeydown={(e) => {
        if (e.key === "Escape" && !modalBusy) closeModal();
      }}
      role="presentation"
      transition:fade={{ duration: 150 }}
    >
      <div
        class="bsplus-cal-modal"
        role="dialog"
        aria-modal="true"
        aria-labelledby="bsplus-cal-modal-title"
        transition:fade={{ duration: 180 }}
      >
        {#if showDisconnect}
          <h2 id="bsplus-cal-modal-title" class="bsplus-cal-modal-title">
            Disconnect {providerLabel} Calendar?
          </h2>
          <p class="bsplus-cal-modal-body">
            Disconnecting normally removes synced classes from {providerLabel} Calendar and signs you out.
            Keep this tab open while that runs. You can also disconnect without deleting your events.
          </p>
          <div class="bsplus-cal-modal-actions bsplus-cal-modal-actions--disconnect">
            <button
              type="button"
              class="bsplus-cal-btn bsplus-cal-btn--ghost"
              disabled={modalBusy}
              onclick={closeModal}
            >
              Cancel
            </button>
            <button
              type="button"
              class="bsplus-cal-btn bsplus-cal-btn--ghost"
              disabled={modalBusy}
              onclick={() => void confirmDisconnect(false)}
            >
              {modalBusy ? "Disconnecting…" : "Disconnect without deleting"}
            </button>
            <button
              type="button"
              class="bsplus-cal-btn bsplus-cal-btn--danger"
              disabled={modalBusy}
              onclick={() => void confirmDisconnect(true)}
            >
              Disconnect & delete classes
            </button>
          </div>
        {:else}
          <h2 id="bsplus-cal-modal-title" class="bsplus-cal-modal-title">
            Delete synced classes?
          </h2>
          <p class="bsplus-cal-modal-body">
            Removes every BetterSEQTA+ timetable event from your {providerLabel} Calendar for this school.
            Your account stays connected — use Update calendar to sync again.
          </p>
          <div class="bsplus-cal-modal-actions">
            <button
              type="button"
              class="bsplus-cal-btn bsplus-cal-btn--ghost"
              disabled={modalBusy}
              onclick={closeModal}
            >
              Cancel
            </button>
            <button
              type="button"
              class="bsplus-cal-btn bsplus-cal-btn--danger"
              disabled={modalBusy}
              onclick={() => void confirmDeleteEvents()}
            >
              {modalBusy ? "Deleting…" : "Delete synced classes"}
            </button>
          </div>
        {/if}
      </div>
    </div>
  {/if}

  {#if disconnectOverlay}
    <div
      class="bsplus-cal-disconnect-overlay"
      bind:this={overlayEl}
      use:portalToBody
      role="alertdialog"
      aria-modal="true"
      aria-labelledby="bsplus-cal-disconnect-title"
      aria-describedby="bsplus-cal-disconnect-desc"
      transition:fade={{ duration: 150 }}
    >
      <div class="bsplus-cal-disconnect-card">
        <h2 id="bsplus-cal-disconnect-title" class="bsplus-cal-disconnect-title">
          Do not close this tab
        </h2>
        <p id="bsplus-cal-disconnect-desc" class="bsplus-cal-disconnect-body">
          Removing synced classes and disconnecting {providerLabel} Calendar. Closing SEQTA now may
          leave events behind or interrupt sign-out.
        </p>
        <div
          class="bsplus-cal-disconnect-progress"
          role="progressbar"
          aria-valuemin="0"
          aria-valuemax="100"
          aria-valuenow={overlayProgressPercent}
          aria-label={overlayStatusText}
        >
          <span
            class="bsplus-cal-disconnect-progress-fill"
            style:width="{overlayProgressPercent}%"
          ></span>
        </div>
        <p class="bsplus-cal-disconnect-status">{overlayStatusText}</p>
      </div>
    </div>
  {/if}

  {#if toast}
    <div
      class="bsplus-cal-toast"
      class:bsplus-cal-toast--error={toast.error}
      role="status"
      transition:fade={{ duration: 150 }}
    >
      {toast.message}
    </div>
  {/if}
</div>

{#snippet googleIcon()}
  <svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="-3 0 262 262"
    preserveAspectRatio="xMidYMid"
    aria-hidden="true"
  >
    <path
      d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
      fill="#4285F4"
    />
    <path
      d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
      fill="#34A853"
    />
    <path
      d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782"
      fill="#FBBC05"
    />
    <path
      d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
      fill="#EB4335"
    />
  </svg>
{/snippet}

{#snippet googleName()}
  Google Calendar
{/snippet}

{#snippet outlookIcon()}
  <OutlookCalendarIcon />
{/snippet}

{#snippet outlookName()}
  Outlook Calendar
{/snippet}

<style>
  .bsplus-cal-sync {
    position: relative;
    display: inline-flex;
    font-family: var(--bsplus-cal-font-family, var(--betterseqta-font-family, Rubik), sans-serif);
    color: var(--bsplus-cal-text, var(--text-primary, #111));
  }

  .bsplus-cal-trigger {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-width: auto;
    height: auto;
    padding: 0 10px;
    margin: 0;
    border-radius: 16px !important;
    font-family: inherit;
    background: var(--bsplus-cal-surface, var(--background-primary, #232323)) !important;
    transition:
      transform 0.2s ease,
      opacity 0.2s ease,
      background-color 0.2s ease;
    overflow: hidden;
    isolation: isolate;
  }

  .bsplus-cal-trigger-fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--bsplus-cal-trigger-progress, 0%);
    border-radius: inherit;
    background: color-mix(
      in srgb,
      var(--bsplus-cal-accent, var(--better-main, #3b82f6)) 38%,
      transparent
    );
    transition: width 0.25s ease;
    pointer-events: none;
  }

  .bsplus-cal-trigger-content {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
  }

  .bsplus-cal-trigger-icon {
    font-family: "IconFamily" !important;
    font-size: 16px;
    line-height: 1;
    opacity: 0.9;
  }

  .bsplus-cal-trigger-text {
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
  }

  .bsplus-cal-trigger:hover:not(.bsplus-cal-trigger--busy) {
    transform: scale(1.03);
  }

  .bsplus-cal-trigger:active:not(.bsplus-cal-trigger--busy) {
    transform: scale(0.97);
  }

  .bsplus-cal-trigger--open,
  .bsplus-cal-trigger--open:hover,
  .bsplus-cal-trigger--open:active {
    background: var(
      --bsplus-cal-surface-muted,
      color-mix(in srgb, var(--bsplus-cal-text, #fff) 12%, var(--bsplus-cal-surface, #232323))
    ) !important;
  }

  .bsplus-cal-trigger--busy {
    opacity: 0.95;
  }

  .bsplus-cal-trigger--progress {
    cursor: default;
  }

  .bsplus-cal-trigger--progress:hover,
  .bsplus-cal-trigger--progress:active {
    transform: none;
  }

  .bsplus-cal-settings {
    margin: 8px 0 10px;
    padding: 10px;
    border-radius: 10px;
    border: 1px solid var(--bsplus-cal-border, color-mix(in srgb, var(--bsplus-cal-text) 12%, transparent));
    background: color-mix(in srgb, var(--bsplus-cal-surface, #fff) 92%, transparent);
    display: grid;
    gap: 8px;
  }

  .bsplus-cal-setting {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
    font-size: 12px;
  }

  .bsplus-cal-setting-copy {
    display: grid;
    gap: 2px;
    min-width: 0;
    flex: 1 1 auto;
  }

  .bsplus-cal-setting-label {
    font-weight: 600;
    color: var(--bsplus-cal-text, var(--text-primary, #111));
  }

  .bsplus-cal-setting-desc {
    font-size: 10px;
    line-height: 1.4;
    color: color-mix(in srgb, var(--bsplus-cal-text, #111) 58%, transparent);
  }

  .bsplus-cal-setting--toggle {
    align-items: center;
  }

  .bsplus-cal-setting-input {
    width: 64px;
    flex: 0 0 auto;
    padding: 6px 8px;
    border-radius: 10px;
    border: 1px solid var(--bsplus-cal-border, color-mix(in srgb, var(--bsplus-cal-text) 18%, transparent));
    background: var(--bsplus-cal-surface, #fff);
    color: var(--bsplus-cal-text, var(--text-primary, #111));
    font-size: 12px;
    text-align: center;
  }

  .bsplus-cal-setting-checkbox {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    accent-color: var(--bsplus-cal-accent, var(--better-main, #3b82f6));
  }

  .bsplus-cal-menu {
    position: fixed;
    z-index: var(--bsplus-cal-z-menu, 1200);
    width: min(320px, calc(100vw - 24px));
    padding: 10px;
    border-radius: 14px;
    background: var(--bsplus-cal-surface, #fff);
    color: var(--bsplus-cal-text, #18181b);
    border: 1px solid var(--bsplus-cal-border, color-mix(in srgb, var(--bsplus-cal-text) 12%, transparent));
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.22);
    font-family: var(--bsplus-cal-font-family, var(--betterseqta-font-family, Rubik), sans-serif);
  }

  .bsplus-cal-menu.dark {
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
  }

  .bsplus-cal-menu-header {
    padding: 6px 8px 10px;
    border-bottom: 1px solid var(--bsplus-cal-border, color-mix(in srgb, var(--bsplus-cal-text) 10%, transparent));
    margin-bottom: 8px;
  }

  .bsplus-cal-menu-title {
    display: block;
    font-size: 13px;
    font-weight: 700;
  }

  .bsplus-cal-menu-sub {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    color: color-mix(in srgb, var(--bsplus-cal-text, #111) 62%, transparent);
  }

  .bsplus-cal-provider {
    padding: 8px;
    border-radius: 10px;
    background: var(--bsplus-cal-surface-muted, color-mix(in srgb, var(--bsplus-cal-text) 4%, transparent));
    margin-bottom: 8px;
  }

  .bsplus-cal-provider-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
  }

  .bsplus-cal-provider-icon {
    display: flex;
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
  }

  .bsplus-cal-provider-icon svg {
    width: 100%;
    height: 100%;
  }

  .bsplus-cal-provider-copy {
    min-width: 0;
  }

  .bsplus-cal-provider-name {
    display: block;
    font-size: 13px;
    font-weight: 600;
  }

  .bsplus-cal-provider-status {
    display: block;
    margin-top: 1px;
    font-size: 11px;
    color: color-mix(in srgb, var(--bsplus-cal-text, #111) 62%, transparent);
  }

  .bsplus-cal-provider-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }

  .bsplus-cal-action {
    flex: 1 1 auto;
    min-width: 0;
    padding: 7px 10px;
    border: none;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .bsplus-cal-action:disabled {
    opacity: 0.55;
    cursor: not-allowed;
  }

  .bsplus-cal-action--primary {
    background: var(--bsplus-cal-accent, var(--better-main, #3b82f6));
    color: #fff;
  }

  .bsplus-cal-action--primary:hover:not(:disabled) {
    filter: brightness(1.06);
    transform: scale(1.02);
  }

  .bsplus-cal-action--ghost {
    background: var(--bsplus-cal-surface-muted, color-mix(in srgb, var(--bsplus-cal-text) 8%, transparent));
    color: var(--bsplus-cal-text, #18181b);
  }

  .bsplus-cal-action--ghost:hover:not(:disabled) {
    background: color-mix(in srgb, var(--bsplus-cal-text) 14%, var(--bsplus-cal-surface));
  }

  .bsplus-cal-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: var(--bsplus-cal-z-modal, 1300);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(4px);
  }

  .bsplus-cal-modal-backdrop.dark .bsplus-cal-modal {
    box-shadow: 0 24px 48px rgba(0, 0, 0, 0.5);
  }

  .bsplus-cal-modal {
    width: min(100%, 400px);
    padding: 20px;
    border-radius: 16px;
    background: var(--bsplus-cal-surface, #fff);
    color: var(--bsplus-cal-text, #111);
    box-shadow: 0 24px 48px rgba(0, 0, 0, 0.22);
    border: 1px solid color-mix(in srgb, var(--bsplus-cal-text, #111) 12%, transparent);
  }

  .bsplus-cal-modal-title {
    margin: 0 0 8px;
    font-size: 18px;
    font-weight: 700;
    line-height: 1.3;
  }

  .bsplus-cal-modal-body {
    margin: 0 0 20px;
    font-size: 14px;
    line-height: 1.5;
    color: color-mix(in srgb, var(--bsplus-cal-text, #111) 72%, transparent);
  }

  .bsplus-cal-modal-actions {
    display: flex;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 8px;
  }

  .bsplus-cal-modal-actions--disconnect {
    justify-content: stretch;
  }

  .bsplus-cal-modal-actions--disconnect .bsplus-cal-btn {
    flex: 1 1 auto;
    min-width: fit-content;
  }

  .bsplus-cal-disconnect-overlay {
    position: fixed;
    inset: 0;
    z-index: calc(var(--bsplus-cal-z-modal, 1300) + 1);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(0, 0, 0, 0.72);
    backdrop-filter: blur(6px);
    pointer-events: all;
  }

  .bsplus-cal-disconnect-card {
    width: min(100%, 420px);
    padding: 24px;
    border-radius: 16px;
    background: var(--bsplus-cal-surface, #fff);
    color: var(--bsplus-cal-text, #111);
    border: 1px solid color-mix(in srgb, var(--bsplus-cal-text, #111) 12%, transparent);
    box-shadow: 0 24px 48px rgba(0, 0, 0, 0.35);
    text-align: center;
  }

  .bsplus-cal-disconnect-title {
    margin: 0 0 8px;
    font-size: 20px;
    font-weight: 700;
    line-height: 1.3;
    color: #dc2626;
  }

  .bsplus-cal-disconnect-body {
    margin: 0 0 20px;
    font-size: 14px;
    line-height: 1.5;
    color: color-mix(in srgb, var(--bsplus-cal-text, #111) 78%, transparent);
  }

  .bsplus-cal-disconnect-progress {
    height: 8px;
    margin: 0 0 10px;
    border-radius: 999px;
    overflow: hidden;
    background: color-mix(in srgb, var(--bsplus-cal-text, #111) 10%, transparent);
  }

  .bsplus-cal-disconnect-progress-fill {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--bsplus-cal-accent, var(--better-main, #3b82f6));
    transition: width 0.25s ease;
  }

  .bsplus-cal-disconnect-status {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: color-mix(in srgb, var(--bsplus-cal-text, #111) 72%, transparent);
  }

  .bsplus-cal-btn {
    padding: 8px 14px;
    border: none;
    border-radius: 10px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .bsplus-cal-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }

  .bsplus-cal-btn--ghost {
    background: color-mix(in srgb, var(--bsplus-cal-text, #111) 8%, transparent);
    color: var(--bsplus-cal-text, #111);
  }

  .bsplus-cal-btn--ghost:hover:not(:disabled) {
    background: color-mix(in srgb, var(--bsplus-cal-text, #111) 14%, transparent);
  }

  .bsplus-cal-btn--danger {
    background: #dc2626;
    color: #fff;
  }

  .bsplus-cal-btn--danger:hover:not(:disabled) {
    background: #b91c1c;
  }

  .bsplus-cal-toast {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: var(--bsplus-cal-z-modal, 1300);
    max-width: min(360px, calc(100vw - 32px));
    padding: 12px 14px;
    border-radius: 12px;
    background: rgba(20, 20, 20, 0.92);
    color: #fff;
    font-size: 13px;
    line-height: 1.4;
    box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25);
    pointer-events: none;
  }

  .bsplus-cal-toast--error {
    background: rgba(120, 24, 24, 0.95);
  }
</style>
