import type { ChatConversationSummary } from '../../mainChatPanel/types';
import { resolveMergedConversationTitle } from '../../mainChatPanel/conversationTitle';
import {
  resolveConversationUnreadMergeState,
  resolveStoredConversationUnreadState,
} from '../../mainChatPanel/conversationUnread';

function shouldPreserveRequestMetadata(
  previousItem: Pick<ChatConversationSummary, 'currentRequestId'>,
  nextItem: Pick<ChatConversationSummary, 'currentRequestId'>,
) {
  const previousRequestId = previousItem.currentRequestId?.trim() || '';
  const nextRequestId = nextItem.currentRequestId?.trim() || '';
  return Boolean(previousRequestId && previousRequestId === nextRequestId);
}

function toConversationSortTime(value: string | null | undefined) {
  if (typeof value !== 'string' || !value.trim()) {
    return 0;
  }

  const parsed = Date.parse(value);
  return Number.isNaN(parsed) ? 0 : parsed;
}

function getConversationLastMessageSortTime(item: ChatConversationSummary) {
  const lastMessageTime = toConversationSortTime(item.lastMessageAt);

  if (lastMessageTime > 0) {
    return lastMessageTime;
  }

  return Math.max(toConversationSortTime(item.createdAt), toConversationSortTime(item.updatedAt));
}

function pickPreferredConversationSummary(left: ChatConversationSummary, right: ChatConversationSummary) {
  const leftTime = getConversationLastMessageSortTime(left);
  const rightTime = getConversationLastMessageSortTime(right);

  if (rightTime !== leftTime) {
    return rightTime > leftTime ? right : left;
  }

  const leftUpdatedAt = toConversationSortTime(left.updatedAt);
  const rightUpdatedAt = toConversationSortTime(right.updatedAt);

  if (rightUpdatedAt !== leftUpdatedAt) {
    return rightUpdatedAt > leftUpdatedAt ? right : left;
  }

  return right;
}

function mergeConversationSummaries(existing: ChatConversationSummary, incoming: ChatConversationSummary) {
  const preferred = pickPreferredConversationSummary(existing, incoming);
  const fallback = preferred === existing ? incoming : existing;

  return {
    ...fallback,
    ...preferred,
    clientId: preferred.clientId ?? fallback.clientId,
    isDraftOnly: preferred.isDraftOnly ?? fallback.isDraftOnly,
    draftText: preferred.draftText ?? fallback.draftText ?? '',
    title: resolveMergedConversationTitle(fallback.title, preferred.title),
    requestBadgeLabel: preferred.requestBadgeLabel?.trim() || fallback.requestBadgeLabel?.trim() || null,
    codexModel: preferred.codexModel?.trim() || fallback.codexModel?.trim() || null,
    chatTypeId: preferred.chatTypeId?.trim() || fallback.chatTypeId?.trim() || null,
    lastChatTypeId: preferred.lastChatTypeId?.trim() || fallback.lastChatTypeId?.trim() || null,
    generalSectionName: preferred.generalSectionName?.trim() || fallback.generalSectionName?.trim() || null,
    contextLabel: preferred.contextLabel?.trim() || fallback.contextLabel?.trim() || null,
    contextDescription: preferred.contextDescription?.trim() || fallback.contextDescription?.trim() || null,
    roomScope: preferred.roomScope ?? fallback.roomScope ?? null,
    notifyOffline: preferred.notifyOffline ?? fallback.notifyOffline,
    hasUnreadResponse: resolveConversationUnreadMergeState(existing, incoming),
    currentRequestId: preferred.currentRequestId?.trim() || fallback.currentRequestId?.trim() || null,
    currentJobStatus: preferred.currentJobStatus ?? fallback.currentJobStatus,
    currentJobMessage: preferred.currentJobMessage?.trim() || fallback.currentJobMessage?.trim() || null,
    currentQueueSize: Math.max(preferred.currentQueueSize ?? 0, fallback.currentQueueSize ?? 0),
    currentStatusUpdatedAt:
      preferred.currentStatusUpdatedAt?.trim() || fallback.currentStatusUpdatedAt?.trim() || null,
    isPendingWork: preferred.isPendingWork ?? fallback.isPendingWork,
    pendingWorkReason: preferred.pendingWorkReason ?? fallback.pendingWorkReason,
    lastRequestPreview: preferred.lastRequestPreview.trim() || fallback.lastRequestPreview.trim(),
    lastMessagePreview: preferred.lastMessagePreview.trim() || fallback.lastMessagePreview.trim(),
    lastResponsePreview: preferred.lastResponsePreview.trim() || fallback.lastResponsePreview.trim(),
    createdAt: preferred.createdAt.trim() || fallback.createdAt.trim(),
    updatedAt: preferred.updatedAt.trim() || fallback.updatedAt.trim(),
    lastMessageAt: preferred.lastMessageAt?.trim() || fallback.lastMessageAt?.trim() || null,
  };
}

function shouldPreserveMissingConversation(item: ChatConversationSummary | null | undefined) {
  if (!item) {
    return false;
  }

  return item.isDraftOnly || item.currentJobStatus === 'queued' || item.currentJobStatus === 'started';
}

function sortChatConversationSummaries(items: ChatConversationSummary[]) {
  const dedupedItems = items.reduce<ChatConversationSummary[]>((result, item) => {
    const sessionId = item.sessionId.trim();

    if (!sessionId) {
      result.push(item);
      return result;
    }

    const existingIndex = result.findIndex((candidate) => candidate.sessionId.trim() === sessionId);

    if (existingIndex < 0) {
      result.push(item);
      return result;
    }

    const nextItems = [...result];
    nextItems[existingIndex] = mergeConversationSummaries(nextItems[existingIndex] as ChatConversationSummary, item);
    return nextItems;
  }, []);

  return dedupedItems.sort((left, right) => {
    const leftTime = getConversationLastMessageSortTime(left);
    const rightTime = getConversationLastMessageSortTime(right);

    if (rightTime !== leftTime) {
      return rightTime - leftTime;
    }

    return left.sessionId.localeCompare(right.sessionId, 'ko-KR');
  });
}

export function mergeConversationItemsPreservingRequestedSession(
  nextItems: ChatConversationSummary[],
  previousItems: ChatConversationSummary[],
  requestedSessionId: string,
) {
  const previousBySessionId = new Map(previousItems.map((item) => [item.sessionId, item] as const));
  const normalizedNextItems = nextItems.map((item) => {
    const previousItem = previousBySessionId.get(item.sessionId);

    if (!previousItem) {
      return {
        ...item,
        hasUnreadResponse: resolveStoredConversationUnreadState(item),
      };
    }

    const preserveRequestMetadata = shouldPreserveRequestMetadata(previousItem, item);

    const chatTypeId = item.chatTypeId?.trim() || previousItem.chatTypeId?.trim() || null;
    const lastChatTypeId =
      item.lastChatTypeId?.trim() ||
      chatTypeId ||
      previousItem.lastChatTypeId?.trim() ||
      previousItem.chatTypeId?.trim() ||
      null;

    return {
      ...item,
      title: resolveMergedConversationTitle(previousItem.title, item.title, {
        preservePrevious: preserveRequestMetadata,
      }),
      requestBadgeLabel: preserveRequestMetadata
        ? previousItem.requestBadgeLabel?.trim() || item.requestBadgeLabel?.trim() || null
        : item.requestBadgeLabel?.trim() || previousItem.requestBadgeLabel?.trim() || null,
      codexModel: item.codexModel?.trim() || previousItem.codexModel?.trim() || null,
      chatTypeId,
      lastChatTypeId,
      generalSectionName: item.generalSectionName?.trim() || previousItem.generalSectionName?.trim() || null,
      contextLabel: item.contextLabel?.trim() || previousItem.contextLabel?.trim() || null,
      contextDescription: item.contextDescription?.trim() || null,
      lastRequestPreview: item.lastRequestPreview.trim() || previousItem.lastRequestPreview.trim(),
      lastMessagePreview: item.lastMessagePreview.trim() || previousItem.lastMessagePreview.trim(),
      lastResponsePreview: item.lastResponsePreview.trim() || previousItem.lastResponsePreview.trim(),
      // For sessions that still exist in the server list, trust the server's
      // current job state instead of reviving stale local queued/started flags.
      currentRequestId: item.currentRequestId?.trim() || null,
      currentJobStatus: item.currentJobStatus ?? null,
      currentJobMessage: item.currentJobMessage?.trim() || null,
      currentQueueSize:
        item.currentJobStatus === 'queued' ? Math.max(1, item.currentQueueSize) : Math.max(0, item.currentQueueSize),
      currentStatusUpdatedAt: item.currentStatusUpdatedAt || null,
    };
  });
  const normalizedRequestedSessionId = requestedSessionId.trim();
  const nextSessionIds = new Set(normalizedNextItems.map((item) => item.sessionId));
  const preservedTransientItems = previousItems.filter((item) => {
    if (!item.sessionId || nextSessionIds.has(item.sessionId)) {
      return false;
    }

    return item.isDraftOnly || item.currentJobStatus === 'queued' || item.currentJobStatus === 'started';
  });

  if (!normalizedRequestedSessionId) {
    return sortChatConversationSummaries([...preservedTransientItems, ...normalizedNextItems]);
  }

  const hasRequestedSession = normalizedNextItems.some((item) => item.sessionId === normalizedRequestedSessionId);

  if (hasRequestedSession) {
    return sortChatConversationSummaries([...preservedTransientItems, ...normalizedNextItems]);
  }

  const preservedRequestedSession =
    previousItems.find((item) => item.sessionId === normalizedRequestedSessionId) ?? null;

  if (!shouldPreserveMissingConversation(preservedRequestedSession)) {
    return sortChatConversationSummaries([...preservedTransientItems, ...normalizedNextItems]);
  }

  if (!preservedRequestedSession) {
    return sortChatConversationSummaries([...preservedTransientItems, ...normalizedNextItems]);
  }

  const hasPreservedRequestedSession = preservedTransientItems.some(
    (item) => item.sessionId === preservedRequestedSession.sessionId,
  );

  if (hasPreservedRequestedSession) {
    return sortChatConversationSummaries([...preservedTransientItems, ...normalizedNextItems]);
  }

  return sortChatConversationSummaries([preservedRequestedSession, ...preservedTransientItems, ...normalizedNextItems]);
}
