'use strict';

const MESSAGE_EVENT_TYPES = new Set(['user', 'assistant']);

function _cleanId(value) {
  const s = String(value || '').trim();
  return s || '';
}

function _addUnique(list, value) {
  const s = _cleanId(value);
  if (s && !list.includes(s)) list.push(s);
}

function _agentIdsFromIdentity(identity) {
  const ids = [];
  if (!identity) return ids;
  if (Array.isArray(identity.agent_session_ids)) {
    for (const id of identity.agent_session_ids) _addUnique(ids, id);
  }
  if (identity.primary_agent_session_id) _addUnique(ids, identity.primary_agent_session_id);
  if (identity.primaryAgent?.agent_session_id) _addUnique(ids, identity.primaryAgent.agent_session_id);
  if (identity.requested_is_agent) _addUnique(ids, identity.requested_id);
  if (Array.isArray(identity.agents)) {
    for (const agent of identity.agents) _addUnique(ids, agent?.agent_session_id);
  }
  return ids;
}

function _agentIdsFromRuntimeSession(session) {
  const ids = [];
  if (!session) return ids;
  _addUnique(ids, session._claudeSessionId);
  _addUnique(ids, session._providerResumeId);
  _addUnique(ids, session.agentSessionId);
  _addUnique(ids, session.agent_session_id);
  _addUnique(ids, session.sessionToken);
  _addUnique(ids, session.chatSessionId);
  if (Array.isArray(session.agentSessionIds)) {
    for (const id of session.agentSessionIds) _addUnique(ids, id);
  }
  if (Array.isArray(session.agent_session_ids)) {
    for (const id of session.agent_session_ids) _addUnique(ids, id);
  }
  return ids;
}

function resolveSessionTimelineIdentity(sessionId, options = {}) {
  const requestedId = _cleanId(sessionId);
  const dbModule = options.dbModule || null;
  const sessions = options.sessions || null;
  let identity = null;

  if (requestedId && dbModule && typeof dbModule.getSessionIdentity === 'function') {
    try { identity = dbModule.getSessionIdentity(requestedId) || null; } catch {}
  }

  const runtimeSession = requestedId && sessions && typeof sessions.get === 'function'
    ? sessions.get(requestedId)
    : null;
  const ctmSessionId = _cleanId(identity?.ctm_session_id)
    || (runtimeSession ? requestedId : '')
    || requestedId;

  const agentSessionIds = [];
  for (const id of _agentIdsFromIdentity(identity)) _addUnique(agentSessionIds, id);
  for (const id of _agentIdsFromRuntimeSession(runtimeSession)) _addUnique(agentSessionIds, id);

  const conversationSourceIds = [];
  if (requestedId && dbModule && typeof dbModule.getSessionConversationSourceIds === 'function') {
    try {
      const ids = dbModule.getSessionConversationSourceIds(requestedId, { includeCtmFallback: true }) || [];
      for (const id of ids) _addUnique(conversationSourceIds, id);
    } catch {}
  }
  for (const id of agentSessionIds) _addUnique(conversationSourceIds, id);
  if (ctmSessionId) _addUnique(conversationSourceIds, ctmSessionId);
  if (requestedId) _addUnique(conversationSourceIds, requestedId);

  const streamLookupIds = [];
  if (requestedId) _addUnique(streamLookupIds, requestedId);
  if (ctmSessionId) _addUnique(streamLookupIds, ctmSessionId);
  for (const id of agentSessionIds) _addUnique(streamLookupIds, id);

  return {
    requestedId,
    ctmSessionId,
    agentSessionIds,
    conversationSourceIds,
    streamLookupIds,
    hasDbIdentity: !!identity,
  };
}

function _eventText(event) {
  return String(event?.text ?? event?.data?.text ?? '').trim();
}

function _eventRole(event) {
  const type = String(event?.type || '').trim();
  if (MESSAGE_EVENT_TYPES.has(type)) return type;
  const role = String(event?.role || '').trim();
  return MESSAGE_EVENT_TYPES.has(role) ? role : '';
}

function _eventTimestamp(event) {
  const ts = event?.timestamp ?? event?.data?.timestamp ?? '';
  if (typeof ts === 'number' && Number.isFinite(ts)) return ts;
  const parsed = Date.parse(String(ts || ''));
  return Number.isFinite(parsed) ? parsed : 0;
}

function _eventKey(event) {
  const explicit = _cleanId(event?.eventId || event?.id || event?.uuid);
  if (explicit) return `id:${explicit}`;
  const role = _eventRole(event);
  const text = _eventText(event);
  if (!role || !text) return '';
  return [role, _eventTimestamp(event), text].join('\n');
}

function dedupeTimelineEvents(events, limit = 200) {
  const seen = new Set();
  const out = [];
  for (const event of Array.isArray(events) ? events : []) {
    const key = _eventKey(event);
    if (!key || seen.has(key)) continue;
    seen.add(key);
    out.push(event);
  }
  out.sort((a, b) => {
    const ats = _eventTimestamp(a);
    const bts = _eventTimestamp(b);
    if (ats && bts && ats !== bts) return ats - bts;
    if (ats && !bts) return -1;
    if (!ats && bts) return 1;
    return 0;
  });
  return out.slice(-Math.max(1, Number(limit) || 200));
}

function collectTimelineStreamEvents(identity, options = {}) {
  const getRecentEvents = options.getRecentEvents;
  const limit = Math.max(1, Number(options.limit) || 200);
  if (typeof getRecentEvents !== 'function') return [];
  const ids = Array.isArray(identity?.streamLookupIds) ? identity.streamLookupIds : [];
  const events = [];
  for (const id of ids) {
    try {
      const recent = getRecentEvents(id, limit);
      if (Array.isArray(recent) && recent.length) events.push(...recent);
    } catch {}
  }
  return dedupeTimelineEvents(events, limit);
}

module.exports = {
  collectTimelineStreamEvents,
  dedupeTimelineEvents,
  resolveSessionTimelineIdentity,
};
