/*
|--------------------------------------------------------------------------
| Notifications (shared between the customer and astrologer apps)
|--------------------------------------------------------------------------
|
| Seven endpoints, mounted identically on /api/user/notifications/** and
| /api/astrologer/notifications/**, so one client here serves both sides
| — only the base path and which localStorage token gets passed in differ.
| Envelope is {status, message, data} (boolean status), same family as the
| rest of the astrologer app's endpoints (see normalize() in astrologer.ts).
|
*/

import type { ConsultationRequest } from "./astrologer";

export type NotificationAudience = "user" | "astrologer";

export interface NotificationItem {
  id: number;
  title: string;
  description: string;
  notification_type: number;
  is_read: boolean;
  is_seen: boolean;
  is_new: boolean;
  chat_request_id: number | null;
  call_request_id: number | null;
  read_at: string | null;
  created_at: string;
  time_ago: string;
  [key: string]: any;
}

export interface NotificationCounts {
  total: number;
  unseen: number;
  seen: number;
  new: number;
}

export interface NotificationPagination {
  page: number;
  limit: number;
  totalRecords: number;
  totalPages: number;
}

export interface NotificationListResult {
  success: boolean;
  message?: string;
  list: NotificationItem[];
  counts: NotificationCounts;
  pagination: NotificationPagination | null;
}

interface FetchResult {
  success: boolean;
  data: any;
  message?: string;
}

// A "new chat/call request" notification's chat_request_id/
// call_request_id was assumed to line up with the ring queue's own
// consultation_id, but that's never been confirmed against a real
// response and live testing showed it doesn't reliably match — the
// dropdown and the full list kept falling back to a plain row even for
// a request that's genuinely still open. Try the id match first, but
// fall back to matching the request's customer name against the
// notification's own text (the backend embeds it verbatim, e.g. "sohan
// togwe wants to start a chat with you"), which doesn't depend on that
// unconfirmed field at all. `claimedIds` stops the same still-open
// request from being attached to more than one historical notification
// about the same customer (e.g. three past "New chat request" rows for
// one person, only one of which should show live actions).
const REQUEST_NOTIFICATION_HINT = /wants to (start|chat|talk)|new (chat|call) request|incoming (chat|call)/i;

export function matchLiveConsultationRequest(
  item: NotificationItem,
  openRequests: Map<number, ConsultationRequest>,
  claimedIds: Set<number>,
): ConsultationRequest | undefined {
  const directId = item.chat_request_id ?? item.call_request_id;

  if (directId != null) {
    const direct = openRequests.get(directId);
    if (direct && !claimedIds.has(direct.consultation_id)) {
      claimedIds.add(direct.consultation_id);
      return direct;
    }
  }

  const text = `${item.title} ${item.description}`;
  if (!REQUEST_NOTIFICATION_HINT.test(text)) return undefined;

  const lowerText = text.toLowerCase();

  for (const request of openRequests.values()) {
    if (claimedIds.has(request.consultation_id)) continue;

    const name = request.consultee_name || request.user_name;
    if (name && lowerText.includes(name.toLowerCase())) {
      claimedIds.add(request.consultation_id);
      return request;
    }
  }

  return undefined;
}

function basePath(audience: NotificationAudience) {
  return audience === "astrologer"
    ? "/api/astrologer/notifications"
    : "/api/user/notifications";
}

async function notificationsFetch(
  url: string,
  token: string | null,
  init: RequestInit = {},
): Promise<FetchResult> {
  if (!token) {
    return { success: false, data: null, message: "Please login first." };
  }

  try {
    const response = await fetch(url, {
      ...init,
      headers: {
        ...(init.headers || {}),
        Authorization: `Bearer ${token}`,
      },
      cache: "no-store",
    });

    const result = await response.json().catch(() => null);

    if (!response.ok || result?.status === false) {
      return {
        success: false,
        data: null,
        message: result?.message || "Unable to reach notifications.",
      };
    }

    return {
      success: true,
      data: result?.data ?? result,
      message: result?.message,
    };
  } catch (error) {
    console.error("Notifications request error:", error);

    return {
      success: false,
      data: null,
      message: "Network error while loading notifications.",
    };
  }
}

export const getNotifications = async (
  audience: NotificationAudience,
  token: string | null,
  params: { page?: number; limit?: number; status?: string; search?: string } = {},
): Promise<NotificationListResult> => {
  const query = new URLSearchParams();

  if (params.page) query.set("page", String(params.page));
  if (params.limit) query.set("limit", String(params.limit));
  if (params.status) query.set("status", params.status);
  if (params.search) query.set("search", params.search);

  const qs = query.toString();

  const result = await notificationsFetch(
    `${basePath(audience)}${qs ? `?${qs}` : ""}`,
    token,
  );

  return {
    success: result.success,
    message: result.message,
    list: Array.isArray(result.data?.list) ? result.data.list : [],
    counts: result.data?.counts ?? { total: 0, unseen: 0, seen: 0, new: 0 },
    pagination: result.data?.pagination ?? null,
  };
};

export const getNotificationCount = async (
  audience: NotificationAudience,
  token: string | null,
): Promise<NotificationCounts> => {
  const result = await notificationsFetch(`${basePath(audience)}/count`, token);

  return result.data ?? { total: 0, unseen: 0, seen: 0, new: 0 };
};

// Clears the tab's "new" dot without marking anything read — entries
// still render unread until individually tapped (see notifications/read).
export const markNotificationsSeen = (
  audience: NotificationAudience,
  token: string | null,
) => notificationsFetch(`${basePath(audience)}/seen`, token, { method: "POST" });

export const markNotificationsRead = (
  audience: NotificationAudience,
  token: string | null,
  body: { ids: number[] } | { all: true },
) =>
  notificationsFetch(`${basePath(audience)}/read`, token, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });

export const deleteNotifications = (
  audience: NotificationAudience,
  token: string | null,
  ids: number[],
) =>
  notificationsFetch(`${basePath(audience)}/delete`, token, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ ids }),
  });

export const deleteNotification = (
  audience: NotificationAudience,
  token: string | null,
  id: number,
) => notificationsFetch(`${basePath(audience)}/${id}`, token, { method: "DELETE" });

export const clearNotifications = (
  audience: NotificationAudience,
  token: string | null,
) => notificationsFetch(`${basePath(audience)}/clear`, token, { method: "POST" });
