import { useEffect } from 'react';
import { useToastStore, toast } from '../store/useToastStore';

/**
 * Chrome fires `beforeinstallprompt` when the app qualifies for installation.
 * Not in lib.dom yet, so the shape is declared here.
 */
interface BeforeInstallPromptEvent extends Event {
  prompt: () => Promise<void>;
  userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
}

const SNOOZE_KEY = 'muzick.install-prompt.snoozed-at';
const SNOOZE_MS = 30 * 24 * 60 * 60 * 1000;

// Long enough that the suggestion lands after the app has proved useful, not
// while the first page is still painting.
const DELAY_MS = 20_000;

function snoozed(): boolean {
  try {
    const at = Number(localStorage.getItem(SNOOZE_KEY));
    return Number.isFinite(at) && at > 0 && Date.now() - at < SNOOZE_MS;
  } catch {
    return false; // Private mode or blocked storage — just ask.
  }
}

function snooze() {
  try {
    localStorage.setItem(SNOOZE_KEY, String(Date.now()));
  } catch {
    /* ignore */
  }
}

/**
 * Offers "Add to Home screen" as a toast, once the browser says the app is
 * installable. Declining snoozes the suggestion for a month; installing
 * silences it for good.
 */
export function useInstallPrompt() {
  useEffect(() => {
    // Already installed — the standalone display mode is the reliable signal.
    if (window.matchMedia?.('(display-mode: standalone)').matches) return;
    if (snoozed()) return;

    let deferred: BeforeInstallPromptEvent | null = null;
    let timer: ReturnType<typeof setTimeout> | undefined;
    let unsubscribe: (() => void) | undefined;

    const offer = () => {
      if (!deferred) return;
      const event = deferred;
      let accepted = false;

      const id = toast.info('Muzick runs better from your home screen.', {
        ttl: 0,
        action: {
          label: 'Install',
          onClick: () => {
            accepted = true;
            unsubscribe?.();
            // A prompt can only be shown once per event; drop it either way.
            deferred = null;
            void event.prompt().then(() => event.userChoice).then((choice) => {
              if (choice.outcome === 'dismissed') snooze();
            }).catch(() => snooze());
          },
        },
      });

      // The toast's own close button calls dismiss() directly, so the only way
      // to notice a decline is to watch the toast leave the store.
      unsubscribe = useToastStore.subscribe((state) => {
        if (accepted) return;
        if (!state.toasts.some((t) => t.id === id)) {
          snooze();
          unsubscribe?.();
        }
      });
    };

    const onBeforeInstallPrompt = (e: Event) => {
      // Suppress Chrome's own mini-infobar so only our toast asks.
      e.preventDefault();
      deferred = e as BeforeInstallPromptEvent;
      timer = setTimeout(offer, DELAY_MS);
    };

    const onInstalled = () => {
      deferred = null;
      if (timer) clearTimeout(timer);
      snooze();
    };

    window.addEventListener('beforeinstallprompt', onBeforeInstallPrompt);
    window.addEventListener('appinstalled', onInstalled);

    return () => {
      window.removeEventListener('beforeinstallprompt', onBeforeInstallPrompt);
      window.removeEventListener('appinstalled', onInstalled);
      if (timer) clearTimeout(timer);
      unsubscribe?.();
    };
  }, []);
}
