"use client";

/**
 * useSmartNotifications
 * Runs in the background (mounted once in AppShell) and generates proactive
 * smart notifications for: overdue tasks, today's deadlines, upcoming calendar
 * events, journal reminders, and newly completed achievements.
 *
 * All notifications are stored in the notificationStore (client-side only).
 * General checks run every 10 minutes; due reminders use a faster dedicated
 * poll so time-based reminders feel immediate.
 */

import { useEffect, useRef } from "react";
import { createClient, getBrowserUserId } from "@/lib/supabase/client";
import { showBrowserNotification } from "@/lib/browser-notifications";
import {
  playAchievementUnlock,
  playEventAlert,
  playJournalReminder,
  playReminderDue,
  playSmartAlert,
  playUrgentPing,
} from "@/lib/sounds";
import {
  useNotificationStore,
  type SmartNotification,
} from "@/stores/notification-store";
import { useWorkspaceStore } from "@/stores/workspace-store";

const CHECK_INTERVAL_MS = 10 * 60 * 1000; // 10 min
const MIN_RECHECK_MS    =  4 * 60 * 1000; //  4 min debounce
const REMINDER_CHECK_INTERVAL_MS = 30 * 1000; // due reminders should feel live
const NOTIFICATIONS_CHANGED_EVENT = "mindavio:notifications-changed";

export function useSmartNotifications() {
  const activeTeamId  = useWorkspaceStore((s) => s.activeTeamId);
  const initialized   = useWorkspaceStore((s) => s.initialized);
  const { addSmartNotification, lastCheckedAt, setLastChecked, claimedRewards } =
    useNotificationStore();

  /* avoid double-firing in StrictMode */
  const runningRef = useRef(false);
  const reminderRunningRef = useRef(false);

  useEffect(() => {
    if (!initialized) return;

    async function runChecks() {
      if (runningRef.current) return;

      const now = Date.now();
      if (lastCheckedAt && now - lastCheckedAt < MIN_RECHECK_MS) return;

      runningRef.current = true;
      setLastChecked(now);

      try {
        const userId   = await getBrowserUserId().catch(() => null);
        if (!userId) return;

        await Promise.allSettled([
          checkOverdueTasks(userId, activeTeamId, now),
          checkDeadlineTasksToday(userId, activeTeamId, now),
          checkDeadlineTasksTomorrow(userId, activeTeamId, now),
          checkUpcomingEvents(userId, activeTeamId, now),
          checkJournalReminder(userId, now),
          checkCompletedAchievements(userId, activeTeamId, now, claimedRewards),
        ]);
      } finally {
        runningRef.current = false;
      }
    }

    async function runReminderChecks() {
      if (reminderRunningRef.current) return;

      reminderRunningRef.current = true;
      try {
        const userId = await getBrowserUserId().catch(() => null);
        if (!userId) return;

        await checkDueReminders(userId, activeTeamId, Date.now());
      } finally {
        reminderRunningRef.current = false;
      }
    }

    void runChecks();
    void runReminderChecks();
    const interval = window.setInterval(() => void runChecks(), CHECK_INTERVAL_MS);
    const reminderInterval = window.setInterval(
      () => void runReminderChecks(),
      REMINDER_CHECK_INTERVAL_MS,
    );

    return () => {
      window.clearInterval(interval);
      window.clearInterval(reminderInterval);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [initialized, activeTeamId]);

  /* ── Individual checkers ────────────────────────────────────────────── */

  async function checkOverdueTasks(userId: string, teamId: string | null, now: number) {
    const supabase = createClient();
    const today    = new Date().toISOString().slice(0, 10);

    const query = supabase
      .from("tasks")
      .select("id, title")
      .not("status", "in", '("done","cancelled")')
      .lt("deadline", today)
      .not("deadline", "is", null);

    if (teamId) query.eq("team_id", teamId);
    else        query.eq("user_id", userId);

    const { data } = await query.limit(20);
    if (!data || data.length === 0) return;

    const id = `overdue-tasks-${userId}-${today}`;
    push({
      id,
      type:      "task_overdue",
      level:     "urgent",
      title:     `${data.length} überfällige Aufgabe${data.length > 1 ? "n" : ""}`,
      body:      data.slice(0, 3).map((t: { title: string }) => t.title).join(", ") + (data.length > 3 ? ` +${data.length - 3}` : ""),
      link:      "/tasks",
      createdAt: now,
    }, playUrgentPing);
  }

  async function checkDeadlineTasksToday(userId: string, teamId: string | null, now: number) {
    const supabase = createClient();
    const today    = new Date().toISOString().slice(0, 10);

    const query = supabase
      .from("tasks")
      .select("id, title")
      .not("status", "in", '("done","cancelled")')
      .eq("deadline", today);

    if (teamId) query.eq("team_id", teamId);
    else        query.eq("user_id", userId);

    const { data } = await query.limit(20);
    if (!data || data.length === 0) return;

    const id = `deadline-today-${userId}-${today}`;
    push({
      id,
      type:      "task_deadline_today",
      level:     "warning",
      title:     `${data.length} Aufgabe${data.length > 1 ? "n" : ""} läuft heute ab`,
      body:      data.slice(0, 3).map((t: { title: string }) => t.title).join(", ") + (data.length > 3 ? ` +${data.length - 3}` : ""),
      link:      "/tasks",
      createdAt: now,
    }, playSmartAlert);
  }

  async function checkDeadlineTasksTomorrow(userId: string, teamId: string | null, now: number) {
    const supabase  = createClient();
    const tomorrow  = new Date(Date.now() + 86400_000).toISOString().slice(0, 10);

    const query = supabase
      .from("tasks")
      .select("id, title")
      .not("status", "in", '("done","cancelled")')
      .eq("deadline", tomorrow);

    if (teamId) query.eq("team_id", teamId);
    else        query.eq("user_id", userId);

    const { data } = await query.limit(20);
    if (!data || data.length === 0) return;

    const id = `deadline-tomorrow-${userId}-${tomorrow}`;
    push({
      id,
      type:      "task_deadline_tomorrow",
      level:     "info",
      title:     `${data.length} Aufgabe${data.length > 1 ? "n" : ""} fällig morgen`,
      body:      data.slice(0, 3).map((t: { title: string }) => t.title).join(", ") + (data.length > 3 ? ` +${data.length - 3}` : ""),
      link:      "/tasks",
      createdAt: now,
    }, playSmartAlert);
  }

  async function checkUpcomingEvents(userId: string, teamId: string | null, now: number) {
    const supabase   = createClient();
    const windowEnd  = new Date(now + 60 * 60_000).toISOString(); // next 60 min
    const windowStart = new Date(now + 5 * 60_000).toISOString();  // ignore events in <5 min

    const query = supabase
      .from("calendar_events")
      .select("id, title, start_time")
      .gt("start_time", windowStart)
      .lt("start_time", windowEnd)
      .eq("all_day", false);

    if (teamId) query.eq("team_id", teamId);
    else        query.eq("user_id", userId);

    const { data } = await query.limit(5);
    if (!data || data.length === 0) return;

    for (const event of data) {
      const minutesUntil = Math.round((new Date(event.start_time).getTime() - now) / 60_000);
      const id = `event-${event.id}-upcoming`;
      push({
        id,
        type:      "event_upcoming",
        level:     minutesUntil <= 15 ? "warning" : "info",
        title:     `Bald: ${event.title}`,
        body:      `Beginnt in ${minutesUntil} Minute${minutesUntil !== 1 ? "n" : ""}`,
        link:      "/calendar",
        createdAt: now,
      }, minutesUntil <= 15 ? playUrgentPing : playEventAlert);
    }
  }

  async function checkJournalReminder(userId: string, now: number) {
    const hour = new Date().getHours();
    if (hour < 9) return; // only after 9 AM

    const supabase = createClient();
    const today    = new Date().toISOString().slice(0, 10);
    const id       = `journal-today-${userId}-${today}`;

    const { data } = await supabase
      .from("journal_entries")
      .select("id, content")
      .eq("user_id", userId)
      .eq("entry_date", today)
      .limit(1);

    const hasEntry = data && data.length > 0;
    // Consider entry empty if content is the default TipTap empty doc
    const isEmpty = !hasEntry || JSON.stringify((data[0] as { content: unknown }).content).length < 30;

    if (!isEmpty) return;

    push({
      id,
      type:      "journal_reminder",
      level:     "info",
      title:     "Journal für heute noch offen",
      body:      "Nimm dir 2 Minuten für deinen täglichen Rückblick.",
      link:      "/journal",
      createdAt: now,
    }, playJournalReminder);
  }

  async function checkDueReminders(userId: string, teamId: string | null, now: number) {
    const supabase = createClient();
    const nowIso = new Date(now).toISOString();

    const scheduledQuery = supabase
      .from("reminders")
      .select("id, title, body, remind_at, snoozed_until, team_id")
      .eq("user_id", userId)
      .eq("status", "scheduled")
      .lte("remind_at", nowIso);

    const snoozedQuery = supabase
      .from("reminders")
      .select("id, title, body, remind_at, snoozed_until, team_id")
      .eq("user_id", userId)
      .eq("status", "snoozed")
      .lte("snoozed_until", nowIso);

    if (teamId) {
      scheduledQuery.eq("team_id", teamId);
      snoozedQuery.eq("team_id", teamId);
    } else {
      scheduledQuery.is("team_id", null);
      snoozedQuery.is("team_id", null);
    }

    const [{ data: scheduledData }, { data: snoozedData }] = await Promise.all([
      scheduledQuery.limit(10),
      snoozedQuery.limit(10),
    ]);

    const dueReminders = [...(scheduledData || []), ...(snoozedData || [])] as Array<{
      body: string | null;
      id: string;
      remind_at: string;
      snoozed_until: string | null;
      team_id: string | null;
      title: string;
    }>;

    if (dueReminders.length === 0) return;

    const { data: claimedData, error: claimError } = await supabase
      .from("reminders")
      .update({ delivered_at: nowIso, status: "triggered" })
      .eq("user_id", userId)
      .in("id", dueReminders.map((reminder) => reminder.id))
      .in("status", ["scheduled", "snoozed"])
      .select("id, title, body, remind_at, snoozed_until, team_id");

    if (claimError) {
      console.warn("Could not claim due reminders", claimError);
      return;
    }

    const claimedReminders = (claimedData || []) as Array<{
      body: string | null;
      id: string;
      remind_at: string;
      snoozed_until: string | null;
      team_id: string | null;
      title: string;
    }>;

    if (claimedReminders.length === 0) return;

    const { error: notificationError } = await supabase
      .from("notifications")
      .insert(
        claimedReminders.map((reminder) => {
          const dueAt = reminder.snoozed_until || reminder.remind_at;

          return {
            actor_id: userId,
            body: reminder.body || "Dieser Reminder ist jetzt fällig.",
            details: {
              due_at: dueAt,
              reminder_id: reminder.id,
            },
            kind: "reminder_due",
            link: "/reminders",
            team_id: reminder.team_id,
            title: `Reminder fällig: ${reminder.title}`,
            user_id: userId,
          };
        }),
      );

    if (notificationError) {
      console.warn("Could not create reminder notifications", notificationError);
    } else {
      window.dispatchEvent(new Event(NOTIFICATIONS_CHANGED_EVENT));
    }

    let didPlayReminderSound = false;

    for (const reminder of claimedReminders) {
      const dueAt = reminder.snoozed_until || reminder.remind_at;
      const title = `Reminder: ${reminder.title}`;
      const body = reminder.body || "Dieser Reminder ist jetzt fällig.";

      showBrowserNotification({
        body,
        tag: `mindavio-reminder-${reminder.id}-${dueAt}`,
        title,
        url: "/reminders",
      });

      if (!didPlayReminderSound) {
        playReminderDue();
        didPlayReminderSound = true;
      }

      if (notificationError) {
        push({
          id: `reminder-${reminder.id}-due-${dueAt}`,
          type: "reminder_due",
          level: "info",
          title,
          body,
          link: "/reminders",
          createdAt: now,
        }, noop);
      }
    }
  }

  async function checkCompletedAchievements(
    userId: string,
    teamId: string | null,
    now: number,
    claimedRewards: Record<string, string>,
  ) {
    const supabase   = createClient();
    const since      = new Date(now - 24 * 3600_000).toISOString(); // last 24h

    const query = supabase
      .from("achievements")
      .select("id, title, reward_title, completed_at")
      .eq("status", "completed")
      .not("completed_at", "is", null)
      .gt("completed_at", since);

    if (teamId) query.eq("team_id", teamId);
    else        query.eq("user_id", userId);

    const { data } = await query.limit(10);
    if (!data || data.length === 0) return;

    for (const a of data) {
      // Achievement completed notification
      const completedId = `achievement-${a.id}-completed`;
      push({
        id:        completedId,
        type:      "achievement_completed",
        level:     "achievement",
        title:     `Achievement abgeschlossen 🏆`,
        body:      a.title,
        link:      `/achievements/${a.id}`,
        createdAt: now,
      }, playAchievementUnlock);

      // Reward claimable notification (only if reward set and not yet claimed)
      if (a.reward_title && !claimedRewards[a.id]) {
        const rewardId = `reward-${a.id}-claimable`;
        push({
          id:        rewardId,
          type:      "reward_claimable",
          level:     "reward",
          title:     "Belohnung bereit zu claimen 🎁",
          body:      a.reward_title,
          link:      `/achievements/${a.id}`,
          createdAt: now,
        }, playAchievementUnlock);
      }
    }
  }

  /* ── Helper: add + play sound only if notification is new ──────────────── */

  function push(notif: SmartNotification, soundFn: () => void) {
    const { smartNotifications, dismissedIds } = useNotificationStore.getState();
    const alreadyExists = smartNotifications.some((n) => n.id === notif.id);
    const wasDismissed  = dismissedIds.includes(notif.id);
    if (!alreadyExists && !wasDismissed) {
      addSmartNotification(notif);
      soundFn();
    }
  }

  function noop() {}
}
