import { accountDisplayName } from "../accounts/account-display.js";
import { notificationTitle } from "./notification-types.js";

function sessionNotification(type, session) {
  const createdAt = session.updatedAt ?? new Date().toISOString();
  const conversationTitle = session.conversationTitle?.trim();
  return {
    id: `${type}:${session.id}:${createdAt}`,
    type,
    title: notificationTitle(type),
    detail: session.projectLabel || "Unknown project",
    accountId: session.accountId,
    sessionId: session.id,
    ...(conversationTitle ? { conversationTitle } : {}),
    createdAt
  };
}

export function detectSessionNotifications(previousSessions, nextSessions, currentSessionId, usageByAccount = new Map()) {
  const previousById = new Map(previousSessions.map((session) => [session.id, session]));
  const notifications = [];

  for (const session of nextSessions) {
    const previous = previousById.get(session.id);
    if (!previous) continue;
    const isCurrentSession = session.id === currentSessionId;
    if (!isCurrentSession && previous.status !== "waiting_approval" && session.status === "waiting_approval") {
      notifications.push(sessionNotification("approval", session));
    } else if (!isCurrentSession && previous.status !== "completed" && session.status === "completed") {
      notifications.push(sessionNotification("completed", session));
    } else if (!isCurrentSession && ["failed", "interrupted"].includes(session.status) && previous.status !== session.status) {
      notifications.push(sessionNotification(usageExhausted(usageByAccount.get(session.accountId)) ? "usage-exhausted" : session.status, session));
    }
  }

  return notifications;
}

export function detectCurrentSessionCompletion(previousSession, nextSession) {
  if (!previousSession || !nextSession || previousSession.id !== nextSession.id) return null;
  if (previousSession.status === "completed" || nextSession.status !== "completed") return null;
  return sessionNotification("completed", nextSession);
}

export function mergeSessionUpdate(sessions = [], updatedSession) {
  if (!updatedSession?.id) return sessions;
  const index = sessions.findIndex((session) => session.id === updatedSession.id);
  if (index < 0) return [...sessions, updatedSession];
  return sessions.map((session, itemIndex) => itemIndex === index ? updatedSession : session);
}

export function mergeSessionSnapshot(currentSessions = [], incomingSessions = []) {
  const currentById = new Map(currentSessions.map((session) => [session.id, session]));
  return incomingSessions.map((incoming) => {
    const current = currentById.get(incoming.id);
    if (!current) return incoming;
    const currentUpdatedAt = Date.parse(current.updatedAt ?? "");
    const incomingUpdatedAt = Date.parse(incoming.updatedAt ?? "");
    return Number.isFinite(currentUpdatedAt) && Number.isFinite(incomingUpdatedAt) && currentUpdatedAt > incomingUpdatedAt
      ? current
      : incoming;
  });
}

function usageExhausted(usage) {
  return [usage?.primary, usage?.secondary].some((window) => Number.isFinite(window?.remainingPercent) && window.remainingPercent <= 0);
}

export function detectWeeklyReset(account, previousUsage, nextUsage, createdAt = new Date().toISOString()) {
  const previous = previousUsage?.secondary;
  const next = nextUsage?.secondary;
  if (!previous || !next) return null;
  if (!previous.resetsAt || !next.resetsAt || next.resetsAt <= previous.resetsAt) return null;
  if (!Number.isFinite(previous.remainingPercent) || !Number.isFinite(next.remainingPercent)) return null;
  if (next.remainingPercent <= previous.remainingPercent) return null;
  const previousResetAt = Date.parse(previous.resetsAt);
  const detectedAt = Date.parse(createdAt);
  if (!Number.isFinite(previousResetAt) || !Number.isFinite(detectedAt) || detectedAt < previousResetAt) return null;

  return {
    id: `weekly-reset:${account.id}:${next.resetsAt}`,
    type: "weekly-reset",
    title: notificationTitle("weekly-reset"),
    detail: `${accountDisplayName(account)} · ${next.remainingPercent}% 사용 가능`,
    accountId: account.id,
    sessionId: null,
    createdAt
  };
}

function usageThresholdNotifications(account, previousUsage, nextUsage, createdAt) {
  return ["primary", "secondary"].flatMap((windowKey) => {
    const previous = previousUsage?.[windowKey];
    const next = nextUsage?.[windowKey];
    if (!Number.isFinite(previous?.remainingPercent) || !Number.isFinite(next?.remainingPercent)) return [];
    const resetKey = next.resetsAt || "unknown";
  const detail = `${windowKey === "primary" ? "5h" : "Weekly"} limit ${next.remainingPercent}% remaining`;
    if (previous.remainingPercent > 5 && next.remainingPercent <= 5) {
      return [usageThresholdNotification("usage-critical", account, windowKey, resetKey, detail, createdAt)];
    }
    if (previous.remainingPercent > 20 && next.remainingPercent <= 20) {
      return [usageThresholdNotification("usage-low", account, windowKey, resetKey, detail, createdAt)];
    }
    return [];
  });
}

function usageThresholdNotification(type, account, windowKey, resetKey, detail, createdAt) {
  return {
    id: `${type}:${account.id}:${windowKey}:${resetKey}`,
    type,
    title: notificationTitle(type),
    detail,
    accountId: account.id,
    sessionId: null,
    createdAt
  };
}

export function collectNotificationPoll({
  accounts,
  results,
  previousSessions,
  previousUsage,
  currentNotifications = [],
  currentSessionId,
  detectedAt = new Date().toISOString()
}) {
  const notifications = [];
  const errors = [];
  const nextUsage = new Map(previousUsage);
  const sessionResult = results[0];
  const usageByAccount = new Map();
  let sessions;

  accounts.forEach((account, index) => {
    const result = results[index + 1];
    if (result?.status === "fulfilled") usageByAccount.set(account.id, result.value);
  });

  if (sessionResult?.status === "fulfilled") {
    sessions = sessionResult.value.sessions ?? [];
    if (previousSessions) {
      notifications.push(...detectSessionNotifications(previousSessions, sessions, currentSessionId, usageByAccount));
    }
  } else {
    errors.push({ kind: "sessions", message: "Notification refresh partially failed." });
  }

  if (sessions) {
    notifications.push(...upgradeUsageExhaustedNotifications(currentNotifications, sessions, usageByAccount));
  }

  const exhaustedAccountIds = new Set(notifications
    .filter((notification) => notification.type === "usage-exhausted")
    .map((notification) => notification.accountId));

  accounts.forEach((account, index) => {
    const result = results[index + 1];
    if (result?.status !== "fulfilled") {
      errors.push({ kind: "usage", accountId: account.id, message: "Notification refresh partially failed." });
      return;
    }
    const previous = nextUsage.get(account.id);
    if (previous) {
      if (!exhaustedAccountIds.has(account.id)) {
        notifications.push(...usageThresholdNotifications(account, previous, result.value, detectedAt));
      }
      const notification = detectWeeklyReset(account, previous, result.value, detectedAt);
      if (notification) notifications.push(notification);
    }
    nextUsage.set(account.id, result.value);
  });

  return { sessions, previousUsage: nextUsage, notifications, errors };
}

export function upgradeUsageExhaustedNotifications(notifications = [], sessions = [], usageByAccount = new Map()) {
  const sessionsById = new Map(sessions.map((session) => [session.id, session]));
  return notifications.flatMap((notification) => {
    if (!["failed", "interrupted"].includes(notification.type)) return [];
    if (!usageExhausted(usageByAccount.get(notification.accountId))) return [];
    const session = sessionsById.get(notification.sessionId);
    if (!session || !["failed", "interrupted"].includes(session.status)) return [];
    return [sessionNotification("usage-exhausted", session)];
  });
}
