import { MatrixEvent } from 'matrix-js-sdk';
import { IRTCNotificationContent } from 'matrix-js-sdk/lib/matrixrtc/types';

export const RTC_NOTIFICATION_DEFAULT_LIFETIME = 30_000;

// Upper bound on a ring's lifetime. The value is fully attacker-controlled
// (raw peer event content), so without a cap a crafted `lifetime` (e.g. ~23
// days, still under the 32-bit setTimeout ceiling) keeps the incoming-call
// strip + looping ringtone alive for the whole session, defeating the ~30s
// auto-expiry. Mirrors the 5-minute cap the send path already enforces
// (CallWidgetDriver `RTC_RING_LIFETIME_MAX_MS`).
export const RTC_NOTIFICATION_MAX_LIFETIME = 5 * 60 * 1000;

// Resolve a ring's lifetime from event content, clamped to a sane range. A
// missing / non-finite / non-positive value falls back to the default; anything
// larger than the cap is clamped down.
export const getRtcNotificationLifetime = (ev: MatrixEvent): number => {
  const { lifetime } = ev.getContent<IRTCNotificationContent>();
  if (typeof lifetime !== 'number' || !Number.isFinite(lifetime) || lifetime <= 0) {
    return RTC_NOTIFICATION_DEFAULT_LIFETIME;
  }
  return Math.min(lifetime, RTC_NOTIFICATION_MAX_LIFETIME);
};

export const getNotificationEventSendTs = (ev: MatrixEvent): number => {
  const content = ev.getContent<IRTCNotificationContent>();
  const sendTs = content.sender_ts;
  const eventTs = ev.getTs();
  if (sendTs && Math.abs(sendTs - eventTs) >= 15_000) return eventTs;
  return sendTs ?? eventTs;
};

export const isRtcNotificationExpired = (ev: MatrixEvent, now = Date.now()): boolean => {
  const lifetime = getRtcNotificationLifetime(ev);
  return now - getNotificationEventSendTs(ev) > lifetime;
};

// Normalize the legacy '' ↔ 'ROOM' equivalence for room-scoped DM call slots.
// matrix-js-sdk 40.2 introduced a `slotDescription.id` compat hack that
// surfaces 'ROOM' for room-scoped m.call.member memberships (which still go on
// the wire as `call_id: ''`); pre-40.2 SDKs and direct raw-event reads in
// `findCallIdSync` race-recovery paths still yield ''. Without this normalize
// step, a single logical ring acquires three distinct dedup keys depending on
// peer SDK version and whether we read happy-path vs fallback — and registry
// dedup, decline-IDs and atom REMOVE/ADD all key on the result.
export const getIncomingCallKey = (callId: string, roomId: string): string => {
  const normalized = callId === '' ? 'ROOM' : callId;
  return `call_${normalized}_${roomId}`;
};
