<script lang="ts">
  import { beforeNavigate, goto } from "$app/navigation";
  import { tick } from "svelte";
  import UnsavedChangesDialog from "./UnsavedChangesDialog.svelte";

  /**
   * A wrapper component that warns the user before discarding unsaved changes.
   *
   * Handles two exit paths automatically:
   *  1. SvelteKit navigation (back button, sidebar links, goto()) — intercepted
   *     via beforeNavigate; navigation is retried after the user confirms discard.
   *  2. Close button / cancel button — call the `guardClose` slot prop (or the
   *     exported `guardClose()` method via bind:this) instead of your own handler.
   *
   * Usage — modal:
   * ```svelte
   * <EditGuard {isDirty} onDiscard={() => { isOpen = false; isDirty = false; }} let:guardClose>
   *   <ModalShell {isOpen} title="Edit Thing" onClose={guardClose}>
   *     ...
   *   </ModalShell>
   * </EditGuard>
   * ```
   *
   * Usage — inline edit pane:
   * ```svelte
   * <EditGuard {isDirty} onDiscard={() => { isEditing = false; isDirty = false; }} let:guardClose>
   *   <div class="edit-panel">
   *     ...
   *     <button on:click={guardClose}>Cancel</button>
   *   </div>
   * </EditGuard>
   * ```
   */

  /** Whether the wrapped content has unsaved changes. */
  export let isDirty: boolean = false;

  /**
   * Called when the user confirms they want to discard changes.
   * Close your modal / exit edit mode and reset isDirty here.
   */
  export let onDiscard: () => void = () => {};

  /** Confirmation dialog title. */
  export let title: string = "Unsaved Changes";

  /** Confirmation dialog body text. */
  export let message: string =
    "You have unsaved changes. Are you sure you want to leave?";

  let showDialog = false;
  let pendingUrl: string | null = null;
  let bypassing = false;

  // Intercept SvelteKit navigation (links, browser back, goto()).
  beforeNavigate(({ cancel, to }) => {
    if (isDirty && !bypassing) {
      cancel();
      pendingUrl = to?.url.href ?? null;
      showDialog = true;
    }
  });

  /**
   * Call this instead of directly triggering close/cancel.
   * Shows the confirmation dialog when dirty; calls onDiscard immediately when clean.
   *
   * Available as a slot prop (`let:guardClose`) or via `bind:this`.
   */
  export function guardClose() {
    if (isDirty) {
      showDialog = true;
    } else {
      onDiscard();
    }
  }

  async function handleDiscard() {
    showDialog = false;
    const url = pendingUrl;
    pendingUrl = null;
    // Temporarily bypass the navigation guard so the retry doesn't re-trigger it.
    bypassing = true;
    onDiscard();
    await tick();
    bypassing = false;
    if (url) goto(url);
  }

  function handleCancel() {
    showDialog = false;
    pendingUrl = null;
  }
</script>

<slot {guardClose} />

<UnsavedChangesDialog
  isOpen={showDialog}
  {title}
  {message}
  onDiscard={handleDiscard}
  onCancel={handleCancel}
/>
