// Per-user, client-side preferences for the floating "upcoming meeting"
// alert popup (see components/executive-meetings/upcoming-meeting-alert.tsx).
//
// Two consumers live in different parts of the React tree:
//   1. <UpcomingMeetingAlert /> mounted globally from App.tsx — reads
//      `enabled` to decide whether to render at all, and the color trio
//      to theme the panel.
//   2. <AlertPrefsCard /> rendered inside the executive-meetings page
//      "Settings" tab — writes the prefs.
//
// Because they are in different subtrees, we share state through
// localStorage + a tiny pub/sub. The native `storage` event only fires
// across tabs, so we also dispatch a same-tab CustomEvent on every save
// and listen for both.

import { useEffect, useState } from "react";

export type AlertPrefs = {
  enabled: boolean;
  // Hex strings like "#fef3c7". Validated on read.
  bg: string;
  accent: string;
  fg: string;
};

export const DEFAULT_ALERT_PREFS: AlertPrefs = {
  enabled: true,
  // Matches the legacy amber theme so existing users see no change.
  bg: "#fffbeb",
  accent: "#fcd34d",
  fg: "#451a03",
};

export type AlertColorPreset = {
  key: string;
  bg: string;
  accent: string;
  fg: string;
};

// Five hand-picked themes. Keys are stable identifiers used for i18n
// labels (executiveMeetings.alertPrefs.preset.<key>).
export const ALERT_COLOR_PRESETS: AlertColorPreset[] = [
  { key: "amber", bg: "#fffbeb", accent: "#fcd34d", fg: "#451a03" },
  { key: "blue", bg: "#eff6ff", accent: "#60a5fa", fg: "#1e3a8a" },
  { key: "green", bg: "#ecfdf5", accent: "#34d399", fg: "#064e3b" },
  { key: "slate", bg: "#f1f5f9", accent: "#64748b", fg: "#0f172a" },
  { key: "rose", bg: "#fff1f2", accent: "#fb7185", fg: "#881337" },
];

const STORAGE_KEY = "txos.executiveMeetings.alertPrefs.v1";
const SYNC_EVENT = "txos:alert-prefs-changed";

const HEX_RE = /^#[0-9a-fA-F]{3,8}$/;

function isValidHex(v: unknown): v is string {
  return typeof v === "string" && HEX_RE.test(v);
}

function normalize(raw: unknown): AlertPrefs {
  if (!raw || typeof raw !== "object") return { ...DEFAULT_ALERT_PREFS };
  const r = raw as Partial<AlertPrefs>;
  return {
    enabled: typeof r.enabled === "boolean" ? r.enabled : DEFAULT_ALERT_PREFS.enabled,
    bg: isValidHex(r.bg) ? r.bg : DEFAULT_ALERT_PREFS.bg,
    accent: isValidHex(r.accent) ? r.accent : DEFAULT_ALERT_PREFS.accent,
    fg: isValidHex(r.fg) ? r.fg : DEFAULT_ALERT_PREFS.fg,
  };
}

export function loadAlertPrefs(): AlertPrefs {
  if (typeof window === "undefined") return { ...DEFAULT_ALERT_PREFS };
  try {
    const raw = window.localStorage.getItem(STORAGE_KEY);
    if (!raw) return { ...DEFAULT_ALERT_PREFS };
    return normalize(JSON.parse(raw));
  } catch {
    return { ...DEFAULT_ALERT_PREFS };
  }
}

export function saveAlertPrefs(prefs: AlertPrefs): void {
  if (typeof window === "undefined") return;
  const safe = normalize(prefs);
  try {
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(safe));
  } catch {
    /* ignore storage errors (private mode, quota, etc.) */
  }
  // Same-tab fan-out — `storage` event only fires in *other* tabs, so we
  // need our own event for the alert and the settings card to both
  // refresh when the user changes a value within this tab.
  try {
    window.dispatchEvent(
      new CustomEvent<AlertPrefs>(SYNC_EVENT, { detail: safe }),
    );
  } catch {
    /* ignore (CustomEvent unsupported is vanishingly rare) */
  }
}

/**
 * React hook returning the current AlertPrefs and a setter that writes
 * back to localStorage and broadcasts to all subscribers (including the
 * caller). Subscribes to both the same-tab CustomEvent and the cross-tab
 * `storage` event so prefs stay coherent regardless of where they were
 * changed.
 */
export function useAlertPrefs(): [AlertPrefs, (next: AlertPrefs) => void] {
  const [prefs, setPrefs] = useState<AlertPrefs>(() => loadAlertPrefs());

  useEffect(() => {
    const onSync = (e: Event) => {
      const ce = e as CustomEvent<AlertPrefs>;
      if (ce.detail) {
        setPrefs(normalize(ce.detail));
      } else {
        setPrefs(loadAlertPrefs());
      }
    };
    const onStorage = (e: StorageEvent) => {
      if (e.key !== STORAGE_KEY) return;
      setPrefs(loadAlertPrefs());
    };
    window.addEventListener(SYNC_EVENT, onSync);
    window.addEventListener("storage", onStorage);
    return () => {
      window.removeEventListener(SYNC_EVENT, onSync);
      window.removeEventListener("storage", onStorage);
    };
  }, []);

  const update = (next: AlertPrefs) => {
    saveAlertPrefs(next);
    setPrefs(normalize(next));
  };

  return [prefs, update];
}

/**
 * Convert a "#rrggbb" hex (or short "#rgb") to an `rgba(...)` string with
 * the given alpha. Used by the alert popup to derive softer borders/hover
 * states from the chosen accent color so the theme stays cohesive.
 * Returns the input string unchanged if parsing fails (defensive).
 */
export function hexToRgba(hex: string, alpha: number): string {
  if (!isValidHex(hex)) return hex;
  let h = hex.slice(1);
  if (h.length === 3) {
    h = h
      .split("")
      .map((c) => c + c)
      .join("");
  }
  if (h.length !== 6 && h.length !== 8) return hex;
  const r = parseInt(h.slice(0, 2), 16);
  const g = parseInt(h.slice(2, 4), 16);
  const b = parseInt(h.slice(4, 6), 16);
  if (![r, g, b].every((n) => Number.isFinite(n))) return hex;
  const a = Math.max(0, Math.min(1, alpha));
  return `rgba(${r}, ${g}, ${b}, ${a})`;
}
