export const RETENTION_HOURS = 48
export const RETENTION_MS = RETENTION_HOURS * 60 * 60 * 1_000
export const RETENTION_MILLISECONDS = RETENTION_MS

export const EXPIRY_STATES = ['available', 'expired'] as const
export type ExpiryState = (typeof EXPIRY_STATES)[number]

function validDate(value: Date, label: string): Date {
  if (!(value instanceof Date) || !Number.isFinite(value.getTime())) throw new TypeError(`${label} must be a valid Date`)
  return value
}

export function sourceExpiryFromAcceptedAt(acceptedAt: Date): Date {
  return new Date(validDate(acceptedAt, 'acceptedAt').getTime() + RETENTION_MS)
}

export const sourceExpiresAt = sourceExpiryFromAcceptedAt

export function zipExpiryFromCompletedAt(completedAt: Date): Date {
  return new Date(validDate(completedAt, 'completedAt').getTime() + RETENTION_MS)
}

export const zipExpiresAt = zipExpiryFromCompletedAt

export function isExpired(deadline: Date, now: Date = new Date()): boolean {
  return validDate(now, 'now').getTime() >= validDate(deadline, 'deadline').getTime()
}

export function expiryState(deadline: Date, now: Date = new Date()): ExpiryState {
  return isExpired(deadline, now) ? 'expired' : 'available'
}

export function remainingLifetimeMs(deadline: Date, now: Date = new Date()): number {
  return Math.max(0, validDate(deadline, 'deadline').getTime() - validDate(now, 'now').getTime())
}

export function clampSignedUrlTtlSeconds(requestedSeconds: number, zipExpiresAt: Date, now: Date = new Date()): number {
  if (!Number.isSafeInteger(requestedSeconds) || requestedSeconds <= 0) throw new RangeError('TTL must be a positive integer number of seconds')
  return Math.min(requestedSeconds, Math.floor(remainingLifetimeMs(zipExpiresAt, now) / 1_000))
}
