import { defineStore } from "pinia";
import {
  fetchInAppNotifications,
  markAllInAppNotificationsRead,
  markInAppNotificationRead,
} from "../api/modules";
import { PERMS } from "../constants/permissions";
import { useAuthStore } from "./auth";

const canReadInApp = () => {
  const authStore = useAuthStore();
  return authStore.scope === "TENANT" && authStore.hasPermission(PERMS.notification.inAppRead);
};

const IN_APP_POLL_INTERVAL_MS = 30000;
const IN_APP_WS_RECONNECT_MS = 5000;

let inAppRefreshTimer: number | null = null;
let inAppWs: WebSocket | null = null;
let inAppWsReconnectTimer: number | null = null;

const emitUnreadChanged = (unreadCount: number) => {
  if (typeof window === "undefined") {
    return;
  }
  try {
    window.dispatchEvent(new CustomEvent("in-app-unread-changed", { detail: { unreadCount } }));
  } catch (_error) {
    // ignore
  }
};

export const useNotificationStore = defineStore("notification", {
  state: () => ({
    unreadCount: 0,
    notifRows: [] as any[],
    polling: false,
    websocketConnected: false,
  }),
  actions: {
    stopPolling() {
      if (inAppRefreshTimer !== null && typeof window !== "undefined") {
        window.clearInterval(inAppRefreshTimer);
      }
      inAppRefreshTimer = null;
      this.polling = false;
    },
    startPolling() {
      if (typeof window === "undefined" || inAppRefreshTimer !== null || !canReadInApp()) {
        return;
      }
      this.polling = true;
      inAppRefreshTimer = window.setInterval(() => {
        void this.loadUnreadCount();
      }, IN_APP_POLL_INTERVAL_MS);
    },
    closeWebSocket() {
      if (inAppWsReconnectTimer !== null && typeof window !== "undefined") {
        window.clearTimeout(inAppWsReconnectTimer);
      }
      inAppWsReconnectTimer = null;
      if (inAppWs) {
        try {
          inAppWs.close();
        } catch (_error) {
          // ignore
        }
        inAppWs = null;
      }
      this.websocketConnected = false;
    },
    scheduleWsReconnect() {
      if (typeof window === "undefined" || !canReadInApp() || inAppWsReconnectTimer !== null) {
        return;
      }
      inAppWsReconnectTimer = window.setTimeout(() => {
        inAppWsReconnectTimer = null;
        this.ensureWebSocket();
      }, IN_APP_WS_RECONNECT_MS);
    },
    ensureWebSocket() {
      if (typeof window === "undefined" || !canReadInApp()) {
        this.closeWebSocket();
        return;
      }
      if (inAppWs && (inAppWs.readyState === WebSocket.OPEN || inAppWs.readyState === WebSocket.CONNECTING)) {
        return;
      }
      const authStore = useAuthStore();
      if (!authStore.token) {
        this.closeWebSocket();
        return;
      }
      const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
      const wsUrl = `${protocol}//${window.location.host}/ws/notifications?token=${encodeURIComponent(authStore.token)}`;
      try {
        const ws = new WebSocket(wsUrl);
        inAppWs = ws;
        ws.onopen = () => {
          this.websocketConnected = true;
          void this.loadUnreadCount();
        };
        ws.onmessage = (event) => {
          let data: any = null;
          try {
            data = JSON.parse(String(event.data || "{}"));
          } catch (_error) {
            data = null;
          }
          const type = String(data?.type || "");
          if (type === "IN_APP_UNREAD_CHANGED" || type === "IN_APP_NOTIFICATION_NEW") {
            void this.loadUnreadCount();
          }
        };
        ws.onclose = () => {
          inAppWs = null;
          this.websocketConnected = false;
          this.scheduleWsReconnect();
        };
        ws.onerror = () => {
          try {
            ws.close();
          } catch (_error) {
            // ignore
          }
        };
      } catch (_error) {
        this.websocketConnected = false;
        this.scheduleWsReconnect();
      }
    },
    async startRealtime() {
      if (!canReadInApp()) {
        this.stopRealtime();
        this.notifRows = [];
        this.setUnreadCount(0);
        return;
      }
      await this.loadUnreadCount();
      this.startPolling();
      this.ensureWebSocket();
    },
    stopRealtime() {
      this.stopPolling();
      this.closeWebSocket();
    },
    async refreshContext() {
      this.stopRealtime();
      if (!canReadInApp()) {
        this.notifRows = [];
        this.setUnreadCount(0);
        return;
      }
      await this.startRealtime();
    },
    handleVisibilityVisible() {
      if (!canReadInApp()) {
        return;
      }
      void this.loadUnreadCount();
      this.ensureWebSocket();
    },
    reset() {
      this.stopRealtime();
      this.unreadCount = 0;
      this.notifRows = [];
      emitUnreadChanged(0);
    },
    setUnreadCount(nextCount: number) {
      this.unreadCount = Number.isFinite(nextCount) && nextCount >= 0 ? nextCount : 0;
      emitUnreadChanged(this.unreadCount);
    },
    async loadUnreadCount() {
      if (!canReadInApp()) {
        this.setUnreadCount(0);
        return;
      }
      try {
        const resp = await fetchInAppNotifications({ ts: Date.now() });
        const list = resp?.data?.list || [];
        this.setUnreadCount(list.filter((item: any) => String(item?.status || "") === "UNREAD").length);
      } catch (_e) {
        this.setUnreadCount(0);
      }
    },
    async loadRows() {
      if (!canReadInApp()) {
        this.notifRows = [];
        this.setUnreadCount(0);
        return;
      }
      try {
        const resp = await fetchInAppNotifications({ ts: Date.now() });
        const list = Array.isArray(resp?.data?.list) ? resp.data.list : [];
        this.notifRows = list;
        this.setUnreadCount(list.filter((item: any) => String(item?.status || "") === "UNREAD").length);
      } catch (_e) {
        this.notifRows = [];
        this.setUnreadCount(0);
      }
    },
    async markRead(id: number) {
      await markInAppNotificationRead(id);
      await this.loadRows();
    },
    async markAllRead() {
      const resp = await markAllInAppNotificationsRead();
      await this.loadRows();
      return Number(resp?.data?.affected || 0);
    },
  },
});
