"use client";

import { useCallback, useSyncExternalStore } from "react";
import { DEFAULT_NOTIFY_PREFS, type MessageNotifyMode, type NotifyPrefs } from "@slackwsh/core";

export { DEFAULT_NOTIFY_PREFS };
export type { MessageNotifyMode, NotifyPrefs };

const KEY = "slackwsh.notify-prefs.v2";
const LEGACY_KEY = "slackwsh.notify-prefs.v1";
const EVENT = "slackwsh:notify-prefs";
const EMPTY_WORKSPACE = "global";

const cache = new Map<string, { raw: string; value: NotifyPrefs }>();

function normalizedWorkspace(workspaceId: string | number | null | undefined) {
  return workspaceId == null || String(workspaceId) === "" ? EMPTY_WORKSPACE : String(workspaceId);
}

function storageKey(workspaceId: string | number | null | undefined) {
  return `${KEY}:${normalizedWorkspace(workspaceId)}`;
}

function normalize(value: Partial<NotifyPrefs> | null | undefined): NotifyPrefs {
  return {
    messages: value?.messages === "mentions" || value?.messages === "off" ? value.messages : "all",
    calls: value?.calls ?? DEFAULT_NOTIFY_PREFS.calls,
    tasks: value?.tasks ?? DEFAULT_NOTIFY_PREFS.tasks,
    calendar: value?.calendar ?? DEFAULT_NOTIFY_PREFS.calendar,
    sound: value?.sound ?? DEFAULT_NOTIFY_PREFS.sound,
    inAppFlash: value?.inAppFlash ?? DEFAULT_NOTIFY_PREFS.inAppFlash,
  };
}

function read(workspaceId?: string | number | null): NotifyPrefs {
  if (typeof window === "undefined") return DEFAULT_NOTIFY_PREFS;
  const ws = normalizedWorkspace(workspaceId);
  const key = storageKey(ws);
  let raw = window.localStorage.getItem(key) ?? "";
  if (!raw) {
    const legacy = window.localStorage.getItem(LEGACY_KEY);
    if (legacy) {
      raw = legacy;
      try {
        window.localStorage.setItem(key, legacy);
      } catch {
        // The in-memory fallback still works in private/quota modes.
      }
    }
  }

  const cached = cache.get(ws);
  if (cached?.raw === raw) return cached.value;
  let value = DEFAULT_NOTIFY_PREFS;
  if (raw) {
    try {
      value = normalize(JSON.parse(raw) as Partial<NotifyPrefs>);
    } catch {
      value = DEFAULT_NOTIFY_PREFS;
    }
  }
  cache.set(ws, { raw, value });
  return value;
}

function write(workspaceId: string | number | null | undefined, value: NotifyPrefs) {
  if (typeof window === "undefined") return;
  const ws = normalizedWorkspace(workspaceId);
  const next = normalize(value);
  const raw = JSON.stringify(next);
  try {
    window.localStorage.setItem(storageKey(ws), raw);
  } catch {
    // Keep the synchronized in-memory value even if local persistence fails.
  }
  cache.set(ws, { raw, value: next });
  window.dispatchEvent(new CustomEvent(EVENT, { detail: { workspaceId: ws, preferences: next } }));
}

export function getNotifyPrefs(workspaceId?: string | number | null): NotifyPrefs {
  return read(workspaceId);
}

/** Optimistic local cache update; callers persist the same patch through API. */
export function setNotifyPrefs(
  workspaceId: string | number | null | undefined,
  patch: Partial<NotifyPrefs>,
): NotifyPrefs {
  const next = { ...read(workspaceId), ...patch };
  write(workspaceId, next);
  return next;
}

/** Replace the cache with the authoritative account state returned by API. */
export function hydrateNotifyPrefs(
  workspaceId: string | number,
  preferences: Partial<NotifyPrefs>,
): NotifyPrefs {
  const next = normalize(preferences);
  write(workspaceId, next);
  return next;
}

function subscribe(workspaceId: string, onStoreChange: () => void) {
  const onLocal = (event: Event) => {
    const changed = (event as CustomEvent<{ workspaceId?: string }>).detail?.workspaceId;
    if (!changed || changed === workspaceId) onStoreChange();
  };
  const onStorage = (event: StorageEvent) => {
    if (event.key === storageKey(workspaceId)) {
      cache.delete(workspaceId);
      onStoreChange();
    }
  };
  window.addEventListener(EVENT, onLocal);
  window.addEventListener("storage", onStorage);
  return () => {
    window.removeEventListener(EVENT, onLocal);
    window.removeEventListener("storage", onStorage);
  };
}

export function useNotifyPrefs(workspaceId: string | number | null | undefined): NotifyPrefs {
  const ws = normalizedWorkspace(workspaceId);
  const subscribeToWorkspace = useCallback((onChange: () => void) => subscribe(ws, onChange), [ws]);
  const getSnapshot = useCallback(() => read(ws), [ws]);
  return useSyncExternalStore(subscribeToWorkspace, getSnapshot, () => DEFAULT_NOTIFY_PREFS);
}
