import { listen } from "@tauri-apps/api/event";
import {
  isPermissionGranted,
  requestPermission,
  sendNotification,
} from "@tauri-apps/plugin-notification";
import { useEffect, useMemo, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import {
  listNotifications,
  markAllNotificationsRead,
  markNotificationRead,
} from "../../lib/api";
import type { OrchaiNotification } from "../../lib/types";

type NewNotificationEvent = {
  notification: OrchaiNotification;
};

async function showSystemNotification(notification: OrchaiNotification) {
  try {
    let permissionGranted = await isPermissionGranted();
    if (!permissionGranted) {
      const permission = await requestPermission();
      permissionGranted = permission === "granted";
    }

    if (permissionGranted) {
      sendNotification({
        title: notification.title,
        body: notification.message,
      });
    }
  } catch {
    // Best effort only
  }
}

export default function NotificationCenter() {
  const navigate = useNavigate();
  const { projectId } = useParams();
  const containerRef = useRef<HTMLDivElement | null>(null);
  const [open, setOpen] = useState(false);
  const [notifications, setNotifications] = useState<OrchaiNotification[]>([]);
  const [filter, setFilter] = useState<"all" | "unread" | "errors" | "fixes">(
    "all"
  );

  async function loadNotifications() {
    if (!projectId) {
      setNotifications([]);
      return;
    }

    try {
      const items = await listNotifications(projectId, false);
      setNotifications(items);
    } catch {
      // Ignore load errors in layout chrome
    }
  }

  useEffect(() => {
    loadNotifications();
  }, [projectId]);

  useEffect(() => {
    let unlisten: (() => void) | null = null;

    const setup = async () => {
      unlisten = await listen<NewNotificationEvent>("new-notification", (event) => {
        const incoming = event.payload.notification;

        if (projectId && incoming.project_id !== projectId) {
          return;
        }

        setNotifications((prev) => {
          const withoutDuplicate = prev.filter((n) => n.id !== incoming.id);
          return [incoming, ...withoutDuplicate];
        });

        void showSystemNotification(incoming);
      });
    };

    void setup();

    return () => {
      if (unlisten) {
        unlisten();
      }
    };
  }, [projectId]);

  useEffect(() => {
    if (!open) {
      return;
    }

    function handleOutsideClick(event: MouseEvent) {
      const target = event.target as Node | null;
      if (!containerRef.current || !target) {
        return;
      }
      if (!containerRef.current.contains(target)) {
        setOpen(false);
      }
    }

    function handleEscape(event: KeyboardEvent) {
      if (event.key === "Escape") {
        setOpen(false);
      }
    }

    document.addEventListener("mousedown", handleOutsideClick);
    document.addEventListener("keydown", handleEscape);
    return () => {
      document.removeEventListener("mousedown", handleOutsideClick);
      document.removeEventListener("keydown", handleEscape);
    };
  }, [open]);

  const unreadCount = useMemo(
    () => notifications.filter((n) => !n.read).length,
    [notifications]
  );

  const filteredNotifications = useMemo(() => {
    switch (filter) {
      case "unread":
        return notifications.filter((n) => !n.read);
      case "errors":
        return notifications.filter((n) => n.notification_type === "Error");
      case "fixes":
        return notifications.filter((n) => n.notification_type === "FixReady");
      default:
        return notifications;
    }
  }, [filter, notifications]);

  async function handleOpenNotification(notification: OrchaiNotification) {
    if (!notification.read) {
      try {
        await markNotificationRead(notification.id);
        setNotifications((prev) =>
          prev.map((n) => (n.id === notification.id ? { ...n, read: true } : n))
        );
      } catch {
        // ignore
      }
    }

    setOpen(false);

    if (notification.ticket_id) {
      navigate(`/tickets/${notification.ticket_id}`);
      return;
    }

    navigate(`/projects/${notification.project_id}`);
  }

  async function handleMarkAllRead() {
    if (!projectId) {
      return;
    }

    try {
      await markAllNotificationsRead(projectId);
      setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
    } catch {
      // ignore
    }
  }

  return (
    <div className="relative" ref={containerRef}>
      <button
        type="button"
        onClick={() => setOpen((v) => !v)}
        className="relative rounded border border-gray-300 bg-white px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50"
      >
        Notifications
        {unreadCount > 0 && (
          <span className="ml-2 rounded-full bg-red-600 px-1.5 py-0.5 text-xs font-semibold text-white">
            {unreadCount}
          </span>
        )}
      </button>

      {open && (
        <div className="absolute right-0 z-20 mt-2 w-[360px] rounded-lg border border-gray-200 bg-white shadow-lg">
          <div className="flex items-center justify-between border-b border-gray-200 px-3 py-2">
            <h3 className="text-sm font-semibold text-gray-800">Notifications</h3>
            <button
              type="button"
              onClick={handleMarkAllRead}
              className="text-xs text-blue-600 hover:underline"
              disabled={!projectId || unreadCount === 0}
            >
              Mark all read
            </button>
          </div>

          <div className="flex gap-1 border-b border-gray-100 px-2 py-2">
            {[
              { id: "all", label: "All" },
              { id: "unread", label: "Unread" },
              { id: "errors", label: "Errors" },
              { id: "fixes", label: "Fixes" },
            ].map((item) => (
              <button
                key={item.id}
                type="button"
                onClick={() =>
                  setFilter(item.id as "all" | "unread" | "errors" | "fixes")
                }
                className={`rounded px-2 py-1 text-xs ${
                  filter === item.id
                    ? "bg-gray-900 text-white"
                    : "bg-gray-100 text-gray-600 hover:bg-gray-200"
                }`}
              >
                {item.label}
              </button>
            ))}
          </div>

          <div className="max-h-[420px] overflow-y-auto">
            {filteredNotifications.length === 0 ? (
              <div className="px-3 py-6 text-center text-sm text-gray-400">
                No notifications.
              </div>
            ) : (
              filteredNotifications.map((notification) => (
                <button
                  key={notification.id}
                  type="button"
                  onClick={() => void handleOpenNotification(notification)}
                  className={`block w-full border-b border-gray-100 px-3 py-2 text-left hover:bg-gray-50 ${
                    notification.read ? "bg-white" : "bg-blue-50/40"
                  }`}
                >
                  <div className="flex items-center justify-between gap-2">
                    <span className="truncate text-sm font-medium text-gray-900">
                      {notification.title}
                    </span>
                    {!notification.read && (
                      <span className="h-2 w-2 shrink-0 rounded-full bg-blue-600" />
                    )}
                  </div>
                  <p className="mt-0.5 line-clamp-2 text-xs text-gray-600">
                    {notification.message}
                  </p>
                  <p className="mt-1 text-[11px] text-gray-400">
                    {new Date(notification.created_at).toLocaleString()}
                  </p>
                </button>
              ))
            )}
          </div>
        </div>
      )}
    </div>
  );
}
