import { useCallback, useEffect, useState } from "react";
import { useAuthToken } from "./use-root-data";
import { getApiBaseUrl } from "~/utils/api-config";

/**
 * Web Push subscription management.
 *
 * Flow: ask permission -> fetch VAPID public key -> subscribe via the service
 * worker's PushManager -> POST the subscription to the backend. Disabling
 * unsubscribes locally and tells the backend to drop the row.
 */

type PermissionState = NotificationPermission | "unsupported";

function urlBase64ToUint8Array(base64String: string): Uint8Array<ArrayBuffer> {
  const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
  const rawData = atob(base64);
  // Back the array with a concrete ArrayBuffer so it satisfies BufferSource.
  const outputArray = new Uint8Array(new ArrayBuffer(rawData.length));
  for (let i = 0; i < rawData.length; i++) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}

function isPushSupported(): boolean {
  return (
    typeof window !== "undefined" &&
    "serviceWorker" in navigator &&
    "PushManager" in window &&
    "Notification" in window
  );
}

export function usePushNotifications() {
  const token = useAuthToken();
  const apiBase = getApiBaseUrl();

  const [permission, setPermission] = useState<PermissionState>("default");
  const [isSubscribed, setIsSubscribed] = useState(false);
  const [busy, setBusy] = useState(false);

  // Reflect current permission + subscription state on mount.
  useEffect(() => {
    if (!isPushSupported()) {
      setPermission("unsupported");
      return;
    }
    setPermission(Notification.permission);
    navigator.serviceWorker.ready
      .then((reg) => reg.pushManager.getSubscription())
      .then((sub) => setIsSubscribed(!!sub))
      .catch(() => setIsSubscribed(false));
  }, []);

  const authHeaders = useCallback(() => {
    const headers: Record<string, string> = { "Content-Type": "application/json" };
    if (token) headers["Authorization"] = `Bearer ${token}`;
    return headers;
  }, [token]);

  const subscribe = useCallback(async (): Promise<boolean> => {
    if (!isPushSupported() || !token) return false;
    setBusy(true);
    try {
      const perm = await Notification.requestPermission();
      setPermission(perm);
      if (perm !== "granted") return false;

      // Get the VAPID public key from the backend. The backend's view returns
      // {"public_key": ...} but its global CamelCaseJSONRenderer rewrites that
      // to {"publicKey": ...} on the wire — so we read camelCase here.
      const keyRes = await fetch(`${apiBase}/api/notif/v1/push/vapid-public-key/`);
      const { publicKey } = await keyRes.json();
      if (!publicKey) return false;

      const reg = await navigator.serviceWorker.ready;
      let sub = await reg.pushManager.getSubscription();
      if (!sub) {
        sub = await reg.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey: urlBase64ToUint8Array(publicKey),
        });
      }

      const res = await fetch(`${apiBase}/api/notif/v1/push/subscribe/`, {
        method: "POST",
        headers: authHeaders(),
        body: JSON.stringify(sub.toJSON()),
      });
      const ok = res.ok;
      setIsSubscribed(ok);
      return ok;
    } catch (e) {
      console.error("Push subscribe failed:", e);
      return false;
    } finally {
      setBusy(false);
    }
  }, [apiBase, token, authHeaders]);

  const disable = useCallback(async (): Promise<void> => {
    if (!isPushSupported()) return;
    setBusy(true);
    try {
      const reg = await navigator.serviceWorker.ready;
      const sub = await reg.pushManager.getSubscription();
      if (sub) {
        await fetch(`${apiBase}/api/notif/v1/push/unsubscribe/`, {
          method: "POST",
          headers: authHeaders(),
          body: JSON.stringify({ endpoint: sub.endpoint }),
        }).catch(() => {});
        await sub.unsubscribe().catch(() => {});
      }
      setIsSubscribed(false);
    } finally {
      setBusy(false);
    }
  }, [apiBase, authHeaders]);

  return {
    supported: permission !== "unsupported",
    permission,
    isSubscribed,
    busy,
    subscribe,
    disable,
  };
}
