"use client";

import { createContext, useCallback, useContext, useMemo, useState } from "react";
import type { AppNotification, NotificationType } from "@/types";
import { useNotificationsList } from "@/hooks/queries/useNotifications";

const READ_KEY = "xremit_notifications_read";

function loadReadIds(): Set<string> {
  try {
    const raw = localStorage.getItem(READ_KEY);
    return raw ? new Set(JSON.parse(raw) as string[]) : new Set();
  } catch {
    return new Set();
  }
}

function saveReadIds(ids: Set<string>) {
  try {
    localStorage.setItem(READ_KEY, JSON.stringify([...ids]));
  } catch {
    /* ignore */
  }
}

function inferType(message: string): NotificationType {
  const m = message.toLowerCase();
  if (/remittance|transfer|send|payment|paid/.test(m)) return "transfer";
  if (/password|2fa|security|login|otp|verification/.test(m)) return "security";
  if (/promo|offer|discount|coupon|bonus/.test(m)) return "promo";
  if (/account|profile|kyc|identity/.test(m)) return "account";
  return "system";
}

// Backend messages arrive with stray double spaces and mid-sentence template
// newlines/indentation (e.g. "amount: 138 USD,\n            Get Amount: ...") —
// collapse all whitespace runs to a single space before deriving title/body.
function normalizeMessage(message: string): string {
  return message.replace(/\s+/g, " ").trim();
}

function extractTitle(message: string): string {
  // Split only on a sentence-ending punctuation mark followed by whitespace —
  // never on a bare "." so decimals inside amounts (e.g. "273.36") stay intact.
  const first = message.split(/(?<=[.!?])\s+/)[0].trim();
  return first.length > 70 ? first.slice(0, 67) + "…" : first;
}

interface NotificationsContextValue {
  notifications: AppNotification[];
  unreadCount: number;
  isLoading: boolean;
  markRead: (id: string) => void;
  markAllRead: () => void;
  markAllUnread: () => void;
}

const NotificationsContext = createContext<NotificationsContextValue | undefined>(undefined);

export function NotificationsProvider({ children }: { children: React.ReactNode }) {
  const { data, isLoading } = useNotificationsList();
  const [readIds, setReadIds] = useState<Set<string>>(loadReadIds);

  const notifications = useMemo<AppNotification[]>(() => {
    const raw = data?.notification ?? [];
    return raw.map((item) => {
      // Composite, not bare `item.id` — the backend's auto-increment id gets reused
      // after a reset/reseed, so a brand-new notification can land on the same id as
      // a previously-read one and appear pre-read. Pairing with created_at keeps the
      // read-tracking key unique to this specific notification instead.
      const id = `${item.id}:${item.created_at}`;
      const normalized = normalizeMessage(item.message);
      return {
        id,
        type: inferType(normalized),
        title: extractTitle(normalized),
        // Strip a stray leading punctuation artifact some backend messages arrive with.
        body: normalized.replace(/^[.,;:\-–—]+\s*/, ""),
        createdAt: item.created_at,
        read: readIds.has(id),
      };
    });
  }, [data, readIds]);

  const markRead = useCallback((id: string) => {
    setReadIds((prev) => {
      const next = new Set(prev);
      next.add(id);
      saveReadIds(next);
      return next;
    });
  }, []);

  const markAllRead = useCallback(() => {
    setReadIds((prev) => {
      const next = new Set(prev);
      (data?.notification ?? []).forEach((n) => next.add(`${n.id}:${n.created_at}`));
      saveReadIds(next);
      return next;
    });
  }, [data]);

  const markAllUnread = useCallback(() => {
    setReadIds((prev) => {
      const next = new Set(prev);
      (data?.notification ?? []).forEach((n) => next.delete(`${n.id}:${n.created_at}`));
      saveReadIds(next);
      return next;
    });
  }, [data]);

  const unreadCount = useMemo(
    () => notifications.filter((n) => !n.read).length,
    [notifications],
  );

  const value = useMemo<NotificationsContextValue>(
    () => ({ notifications, unreadCount, isLoading, markRead, markAllRead, markAllUnread }),
    [notifications, unreadCount, isLoading, markRead, markAllRead, markAllUnread],
  );

  return (
    <NotificationsContext.Provider value={value}>{children}</NotificationsContext.Provider>
  );
}

export function useNotifications() {
  const ctx = useContext(NotificationsContext);
  if (!ctx) throw new Error("useNotifications must be used within NotificationsProvider");
  return ctx;
}
