"use client";

import Link from "next/link";
import { useEffect, useMemo, useState } from "react";

import { Badge } from "@/components/dashboard-ui";
import {
  actionItems,
  calendarEvents,
  complianceItems,
  daysUntil,
  formatDate,
  formatDateTime,
  getPropertyName,
  getPropertyById,
  messageThreads,
  workOrders,
} from "@/lib/property-data-uk";
import {
  type ReminderPreferences,
  REMINDER_PREFERENCES_EVENT,
  defaultReminderPreferences,
  loadReminderPreferences,
} from "@/lib/reminder-preferences";

type NotificationSeverity = "critical" | "warning" | "info" | "success";

type PropertyNotification = {
  id: string;
  title: string;
  message: string;
  category: string;
  severity: NotificationSeverity;
  dueDate?: string;
  timeLabel?: string;
  propertyName?: string;
  href: string;
};

type StoredDocumentReminder = {
  id: string;
  propertyId: string;
  title: string;
  category: string;
  fileName: string;
  reviewDate: string;
  notes: string;
};

const READ_KEY = "propertypilot-notifications-read";
const DISMISSED_KEY = "propertypilot-notifications-dismissed";
const DB_NAME = "propertypilot-document-vault";
const STORE_NAME = "propertyDocuments";

const severityRank: Record<NotificationSeverity, number> = {
  critical: 0,
  warning: 1,
  info: 2,
  success: 3,
};

function BellIcon() {
  return (
    <svg
      aria-hidden="true"
      fill="none"
      height="20"
      stroke="currentColor"
      strokeLinecap="round"
      strokeLinejoin="round"
      strokeWidth="1.8"
      viewBox="0 0 24 24"
      width="20"
    >
      <path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9" />
      <path d="M10 21h4" />
    </svg>
  );
}

function cn(...classes: Array<string | false | null | undefined>) {
  return classes.filter(Boolean).join(" ");
}

function readStringSet(key: string) {
  if (typeof window === "undefined") {
    return new Set<string>();
  }

  try {
    const stored = window.localStorage.getItem(key);
    const parsed = stored ? (JSON.parse(stored) as unknown) : [];

    return new Set(Array.isArray(parsed) ? parsed.filter(Boolean).map(String) : []);
  } catch {
    return new Set<string>();
  }
}

function writeStringSet(key: string, value: Set<string>) {
  window.localStorage.setItem(key, JSON.stringify([...value]));
}

function urgencyText(date: string) {
  const days = daysUntil(date);

  if (days < 0) {
    return `${Math.abs(days)} days overdue`;
  }

  if (days === 0) {
    return "Due today";
  }

  if (days === 1) {
    return "Due tomorrow";
  }

  return `Due in ${days} days`;
}

function dueSeverity(date: string, leadDays: number): NotificationSeverity {
  const days = daysUntil(date);

  if (days < 0) {
    return "critical";
  }

  if (days <= Math.min(leadDays, 7)) {
    return "warning";
  }

  return "info";
}

function sortNotifications(
  left: PropertyNotification,
  right: PropertyNotification,
) {
  const severityDifference = severityRank[left.severity] - severityRank[right.severity];

  if (severityDifference !== 0) {
    return severityDifference;
  }

  const leftDate = left.dueDate ?? "9999-12-31";
  const rightDate = right.dueDate ?? "9999-12-31";

  return leftDate.localeCompare(rightDate);
}

function propertyHref(propertyId?: string) {
  if (!propertyId) {
    return "/app";
  }

  const property = getPropertyById(propertyId);

  return property ? `/properties/${property.slug}` : "/properties";
}

function createBaseNotifications(preferences: ReminderPreferences): PropertyNotification[] {
  const complianceNotifications = preferences.categories.safety
    ? complianceItems.filter((item) => {
      const days = daysUntil(item.dueDate);
      return (
        item.status !== "Completed" &&
        (item.status !== "Queued" || days <= preferences.leadDays)
      );
    }).map<PropertyNotification>((item) => ({
        id: `compliance-${item.id}`,
        title: item.title,
        message:
          item.status === "Blocked"
            ? `${item.owner} needs evidence or a booking before this can move.`
            : `${item.owner} should confirm this before ${formatDate(item.dueDate)}.`,
        category: "Safety",
        severity:
          item.status === "Blocked"
            ? "critical"
            : dueSeverity(item.dueDate, preferences.leadDays),
        dueDate: item.dueDate,
        timeLabel: urgencyText(item.dueDate),
        propertyName: getPropertyName(item.propertyId),
        href: "/compliance",
      }))
    : [];

  const repairNotifications = preferences.categories.repairs
    ? workOrders
        .filter(
          (order) =>
            order.status !== "Resolved" &&
            (order.priority === "Critical" || order.priority === "High"),
        )
        .map<PropertyNotification>((order) => ({
          id: `repair-${order.id}`,
          title: order.title,
          message: `${order.priority} ${order.category.toLowerCase()} job with ${order.assignedTo}. Current status: ${order.status}.`,
          category: "Repairs",
          severity: order.priority === "Critical" ? "critical" : "warning",
          timeLabel: `${order.slaHours}h SLA`,
          propertyName: getPropertyName(order.propertyId),
          href: "/maintenance",
        }))
    : [];

  const messageNotifications = preferences.categories.messages
    ? messageThreads
        .filter((thread) => thread.unread > 0)
        .map<PropertyNotification>((thread) => ({
          id: `message-${thread.id}`,
          title: thread.title,
          message: `${thread.unread} unread ${thread.channel.toLowerCase()} ${
            thread.unread === 1 ? "message" : "messages"
          } from ${thread.participants}.`,
          category: "Messages",
          severity: thread.sentiment === "Needs attention" ? "warning" : "info",
          timeLabel: formatDateTime(thread.lastMessageAt),
          propertyName: getPropertyName(thread.propertyId),
          href: "/communications",
        }))
    : [];

  const taskNotifications = preferences.categories.tasks
    ? actionItems
        .filter(
          (item) =>
            item.tone !== "teal" || daysUntil(item.dueDate) <= preferences.leadDays,
        )
        .map<PropertyNotification>((item) => ({
          id: `task-${item.id}`,
          title: item.title,
          message: item.detail,
          category: item.owner,
          severity: item.tone === "red" ? "critical" : "warning",
          dueDate: item.dueDate,
          timeLabel: urgencyText(item.dueDate),
          href: "/app#today-actions",
        }))
    : [];

  const calendarNotifications = preferences.categories.diary
    ? calendarEvents
        .filter(
          (event) =>
            daysUntil(event.date) >= 0 &&
            daysUntil(event.date) <= preferences.leadDays,
        )
        .map<PropertyNotification>((event) => ({
          id: `event-${event.id}`,
          title: event.title,
          message: `${event.owner} is assigned to this ${event.category.toLowerCase()} diary item.`,
          category: "Diary",
          severity: daysUntil(event.date) <= 3 ? "warning" : "info",
          dueDate: event.date,
          timeLabel: urgencyText(event.date),
          propertyName: event.propertyId ? getPropertyName(event.propertyId) : undefined,
          href: "/maintenance",
        }))
    : [];

  return [
    ...complianceNotifications,
    ...repairNotifications,
    ...messageNotifications,
    ...taskNotifications,
    ...calendarNotifications,
  ].sort(sortNotifications);
}

function readDocumentReminders() {
  return new Promise<StoredDocumentReminder[]>((resolve) => {
    if (typeof window === "undefined" || !("indexedDB" in window)) {
      resolve([]);
      return;
    }

    const request = window.indexedDB.open(DB_NAME, 1);

    request.onerror = () => resolve([]);
    request.onupgradeneeded = () => {
      request.transaction?.abort();
      resolve([]);
    };
    request.onsuccess = () => {
      const database = request.result;
      const transaction = database.transaction(STORE_NAME, "readonly");
      const storeRequest = transaction.objectStore(STORE_NAME).getAll();

      storeRequest.onerror = () => resolve([]);
      storeRequest.onsuccess = () => {
        resolve(storeRequest.result as StoredDocumentReminder[]);
      };
      transaction.oncomplete = () => database.close();
      transaction.onerror = () => {
        database.close();
        resolve([]);
      };
    };
  });
}

function documentToNotification(
  documentRecord: StoredDocumentReminder,
  preferences: ReminderPreferences,
): PropertyNotification | null {
  if (
    !preferences.categories.files ||
    !documentRecord.reviewDate ||
    daysUntil(documentRecord.reviewDate) > preferences.leadDays
  ) {
    return null;
  }

  return {
    id: `document-${documentRecord.id}-${documentRecord.reviewDate}`,
    title: documentRecord.title || documentRecord.fileName,
    message: `${documentRecord.category} needs a review date check. ${
      documentRecord.notes ? documentRecord.notes : "Open the document vault if this has been renewed."
    }`,
    category: "Files",
    severity: dueSeverity(documentRecord.reviewDate, preferences.leadDays),
    dueDate: documentRecord.reviewDate,
    timeLabel: urgencyText(documentRecord.reviewDate),
    propertyName: getPropertyName(documentRecord.propertyId),
    href: propertyHref(documentRecord.propertyId),
  };
}

function severityClasses(severity: NotificationSeverity) {
  return {
    critical: "border-danger/35 bg-danger-soft text-danger",
    warning: "border-accent/35 bg-accent-soft text-accent",
    info: "border-primary/25 bg-primary-soft text-primary",
    success: "border-success/25 bg-success/10 text-success",
  }[severity];
}

function severityLabel(severity: NotificationSeverity) {
  return {
    critical: "Urgent",
    warning: "Soon",
    info: "Info",
    success: "Done",
  }[severity];
}

export function NotificationCentre() {
  const [open, setOpen] = useState(false);
  const [readIds, setReadIds] = useState<Set<string>>(() => new Set());
  const [dismissedIds, setDismissedIds] = useState<Set<string>>(() => new Set());
  const [documentNotifications, setDocumentNotifications] = useState<
    PropertyNotification[]
  >([]);
  const [reminderPreferences, setReminderPreferences] =
    useState<ReminderPreferences>(defaultReminderPreferences);
  const [browserPermission, setBrowserPermission] = useState<
    "default" | "denied" | "granted" | "unsupported"
  >("unsupported");

  const notifications = useMemo(
    () =>
      [
        ...createBaseNotifications(reminderPreferences),
        ...documentNotifications,
      ].sort(sortNotifications),
    [documentNotifications, reminderPreferences],
  );
  const visibleNotifications = notifications.filter(
    (notification) => !dismissedIds.has(notification.id),
  );
  const unreadNotifications = visibleNotifications.filter(
    (notification) => !readIds.has(notification.id),
  );
  const urgentCount = visibleNotifications.filter(
    (notification) => notification.severity === "critical",
  ).length;
  const warningCount = visibleNotifications.filter(
    (notification) => notification.severity === "warning",
  ).length;

  useEffect(() => {
    let active = true;
    const preferencesTimer = window.setTimeout(() => {
      if (!active) {
        return;
      }

      setReadIds(readStringSet(READ_KEY));
      setDismissedIds(readStringSet(DISMISSED_KEY));
      setReminderPreferences(loadReminderPreferences());

      if ("Notification" in window) {
        setBrowserPermission(Notification.permission);
      }
    }, 0);

    async function loadDocumentNotifications(preferences: ReminderPreferences) {
      const reminders = await readDocumentReminders();

      if (!active) {
        return;
      }

      setDocumentNotifications(
        reminders
          .map((reminder) => documentToNotification(reminder, preferences))
          .filter(
            (notification): notification is PropertyNotification => notification !== null,
          ),
      );
    }

    const initialPreferences = loadReminderPreferences();
    loadDocumentNotifications(initialPreferences);

    function handleReminderPreferencesChange() {
      const nextPreferences = loadReminderPreferences();
      setReminderPreferences(nextPreferences);
      loadDocumentNotifications(nextPreferences);
    }

    window.addEventListener(
      REMINDER_PREFERENCES_EVENT,
      handleReminderPreferencesChange,
    );
    window.addEventListener("storage", handleReminderPreferencesChange);

    return () => {
      active = false;
      window.clearTimeout(preferencesTimer);
      window.removeEventListener(
        REMINDER_PREFERENCES_EVENT,
        handleReminderPreferencesChange,
      );
      window.removeEventListener("storage", handleReminderPreferencesChange);
    };
  }, []);

  function markRead(notificationId: string) {
    setReadIds((current) => {
      const next = new Set(current);
      next.add(notificationId);
      writeStringSet(READ_KEY, next);
      return next;
    });
  }

  function dismiss(notificationId: string) {
    setDismissedIds((current) => {
      const next = new Set(current);
      next.add(notificationId);
      writeStringSet(DISMISSED_KEY, next);
      return next;
    });
    markRead(notificationId);
  }

  function markAllRead() {
    const next = new Set(readIds);

    for (const notification of visibleNotifications) {
      next.add(notification.id);
    }

    setReadIds(next);
    writeStringSet(READ_KEY, next);
  }

  async function enableDesktopAlerts() {
    if (!("Notification" in window)) {
      setBrowserPermission("unsupported");
      return;
    }

    const permission = await Notification.requestPermission();
    setBrowserPermission(permission);

    if (permission === "granted") {
      const firstUrgent = visibleNotifications[0];

      if (firstUrgent) {
        new Notification(`PropertyPilot: ${firstUrgent.title}`, {
          body: `${firstUrgent.message} Reminder window: ${reminderPreferences.leadDays} days.`,
        });
      }
    }
  }

  return (
    <div className="relative z-[100]">
      <button
        aria-expanded={open}
        aria-label="Notifications"
        className="relative inline-flex h-11 w-11 items-center justify-center rounded-full border border-border bg-surface-strong text-foreground transition hover:border-primary/35 hover:text-primary"
        onClick={() => setOpen((current) => !current)}
        title="Notifications"
        type="button"
      >
        <BellIcon />
        {unreadNotifications.length > 0 ? (
          <span className="absolute -right-1 -top-1 grid min-h-5 min-w-5 place-items-center rounded-full bg-danger px-1.5 text-[10px] font-black leading-none text-white">
            {unreadNotifications.length > 99 ? "99+" : unreadNotifications.length}
          </span>
        ) : null}
      </button>

      {open ? (
        <div className="absolute right-1/2 top-[calc(100%+0.75rem)] z-[130] w-[min(94vw,560px)] translate-x-1/2 overflow-hidden rounded-[28px] border border-border bg-surface-strong text-left shadow-[0_28px_90px_rgba(0,0,0,0.24)] sm:right-0 sm:translate-x-0">
          <div className="flex items-start justify-between gap-4 bg-surface-deep px-5 py-4 text-white">
            <div>
              <p className="font-mono text-[11px] uppercase tracking-[0.28em] text-white/60">
                Notifications
              </p>
              <h2 className="mt-2 text-xl font-semibold tracking-tight">
                Landlord command inbox
              </h2>
              <p className="mt-1 text-sm text-white/70">
                Bills, certificates, repairs, messages, files, and diary items.
              </p>
            </div>
            <button
              className="rounded-full border border-white/20 px-3 py-1 text-xs font-semibold text-white/80 transition hover:border-white/40 hover:text-white"
              onClick={() => setOpen(false)}
              type="button"
            >
              Close
            </button>
          </div>

          <div className="max-h-[72vh] space-y-4 overflow-y-auto p-5">
            <div className="grid gap-3 sm:grid-cols-3">
              <div className="rounded-[20px] border border-border bg-surface p-4">
                <p className="text-xs uppercase tracking-[0.18em] text-muted">Unread</p>
                <p className="mt-2 text-2xl font-semibold text-foreground">
                  {unreadNotifications.length}
                </p>
              </div>
              <div className="rounded-[20px] border border-border bg-surface p-4">
                <p className="text-xs uppercase tracking-[0.18em] text-muted">Urgent</p>
                <p className="mt-2 text-2xl font-semibold text-danger">{urgentCount}</p>
              </div>
              <div className="rounded-[20px] border border-border bg-surface p-4">
                <p className="text-xs uppercase tracking-[0.18em] text-muted">Soon</p>
                <p className="mt-2 text-2xl font-semibold text-accent">{warningCount}</p>
              </div>
            </div>

            <section className="rounded-[22px] border border-border bg-surface p-4">
              <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                <div>
                  <p className="text-sm font-semibold text-foreground">
                    Desktop alerts
                  </p>
                  <p className="mt-1 text-xs leading-5 text-muted">
                    Optional browser notifications for urgent property reminders on this
                    device.
                  </p>
                </div>
                {browserPermission === "granted" ? (
                  <Badge tone="success">Enabled</Badge>
                ) : (
                  <button
                    className="rounded-full border border-primary bg-primary px-4 py-2 text-sm font-semibold text-white transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-60"
                    disabled={browserPermission === "denied"}
                    onClick={enableDesktopAlerts}
                    type="button"
                  >
                    {browserPermission === "denied"
                      ? "Blocked in browser"
                      : "Enable alerts"}
                  </button>
                )}
              </div>
            </section>

            <div className="flex flex-wrap items-center justify-between gap-3">
              <p className="text-sm font-semibold text-foreground">
                {visibleNotifications.length} active reminders
              </p>
              <button
                className="rounded-full border border-border bg-white/68 px-4 py-2 text-sm font-semibold text-foreground transition hover:border-primary/35 hover:text-primary"
                onClick={markAllRead}
                type="button"
              >
                Mark all read
              </button>
            </div>

            <div className="space-y-3">
              {visibleNotifications.length === 0 ? (
                <div className="rounded-[22px] border border-dashed border-border bg-surface p-5">
                  <p className="font-semibold text-foreground">No active notifications.</p>
                  <p className="mt-2 text-sm leading-6 text-muted">
                    You are clear for now. New overdue checks, unread messages,
                    document review dates, and urgent repairs will appear here.
                  </p>
                </div>
              ) : null}

              {visibleNotifications.map((notification) => {
                const read = readIds.has(notification.id);

                return (
                  <article
                    key={notification.id}
                    className={cn(
                      "rounded-[22px] border bg-surface p-4 transition",
                      read ? "border-border opacity-78" : "border-primary/28",
                    )}
                  >
                    <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
                      <div className="min-w-0">
                        <div className="flex flex-wrap items-center gap-2">
                          <span
                            className={cn(
                              "rounded-full border px-3 py-1 font-mono text-[11px] uppercase tracking-[0.18em]",
                              severityClasses(notification.severity),
                            )}
                          >
                            {severityLabel(notification.severity)}
                          </span>
                          <Badge tone="neutral">{notification.category}</Badge>
                          {!read ? <Badge tone="amber">New</Badge> : null}
                        </div>
                        <h3 className="mt-3 text-base font-semibold text-foreground">
                          {notification.title}
                        </h3>
                        <p className="mt-2 text-sm leading-6 text-muted">
                          {notification.message}
                        </p>
                        <div className="mt-3 flex flex-wrap gap-2 text-xs font-semibold text-muted">
                          {notification.propertyName ? (
                            <span>{notification.propertyName}</span>
                          ) : null}
                          {notification.timeLabel ? (
                            <span>{notification.timeLabel}</span>
                          ) : null}
                        </div>
                      </div>

                      <div className="flex flex-wrap gap-2 sm:justify-end">
                        <Link
                          className="rounded-full border border-border bg-white/68 px-4 py-2 text-sm font-semibold text-foreground transition hover:border-primary/35 hover:text-primary"
                          href={notification.href}
                          onClick={() => markRead(notification.id)}
                        >
                          Open
                        </Link>
                        {!read ? (
                          <button
                            className="rounded-full border border-border bg-white/68 px-4 py-2 text-sm font-semibold text-foreground transition hover:border-primary/35 hover:text-primary"
                            onClick={() => markRead(notification.id)}
                            type="button"
                          >
                            Read
                          </button>
                        ) : null}
                        <button
                          className="rounded-full border border-danger/30 bg-danger-soft px-4 py-2 text-sm font-semibold text-danger transition hover:bg-danger/15"
                          onClick={() => dismiss(notification.id)}
                          type="button"
                        >
                          Dismiss
                        </button>
                      </div>
                    </div>
                  </article>
                );
              })}
            </div>
          </div>
        </div>
      ) : null}
    </div>
  );
}
