sistema-de-chamados/src/components/tickets/ticket-timer.utils.ts
2025-10-19 20:27:11 -03:00

75 lines
2.2 KiB
TypeScript

export type SessionStartOrigin = "unknown" | "remote" | "fresh-local" | "already-running-fallback"
interface ReconcileArgs {
remoteStart: number
localStart: number
origin: SessionStartOrigin
}
interface ReconcileResult {
localStart: number
origin: SessionStartOrigin
}
/**
* Reconcilia o início da sessão local com o timestamp vindo do servidor.
* Mantém a proteção contra timestamps remotos defasados (que geravam tempo inflado),
* mas permite substituir o fallback local quando sabemos que já existia uma sessão em andamento.
*/
export function reconcileLocalSessionStart({ remoteStart, localStart, origin }: ReconcileArgs): ReconcileResult {
const sanitizedRemote = Number(remoteStart) || 0
const sanitizedLocal = Number(localStart) || 0
if (sanitizedRemote <= 0) {
return { localStart: sanitizedLocal, origin }
}
if (sanitizedLocal <= 0) {
return { localStart: sanitizedRemote, origin: "remote" }
}
if (sanitizedRemote >= sanitizedLocal) {
return { localStart: sanitizedRemote, origin: "remote" }
}
if (origin === "already-running-fallback") {
return { localStart: sanitizedRemote, origin: "remote" }
}
return { localStart: sanitizedLocal, origin }
}
interface OffsetArgs {
currentOffset: number
localNow: number
serverNow?: number | null
}
/**
* Calcula o deslocamento entre o relógio do navegador e o do servidor.
* Usamos o menor número de suposições possível: basta termos uma leitura de serverNow
* para alinhar o relógio local.
*/
export function deriveServerOffset({ currentOffset, localNow, serverNow }: OffsetArgs): number {
if (typeof serverNow !== "number" || Number.isNaN(serverNow)) {
return currentOffset
}
const nextOffset = localNow - serverNow
if (!Number.isFinite(nextOffset)) {
return currentOffset
}
if (!Number.isFinite(currentOffset) || currentOffset === 0) {
return nextOffset
}
const MAX_REASONABLE_JUMP = 5 * 60 * 1000 // 5 minutos
if (Math.abs(nextOffset - currentOffset) > MAX_REASONABLE_JUMP) {
return nextOffset
}
return nextOffset
}
export function toServerTimestamp(localNow: number, offset: number): number {
if (!Number.isFinite(localNow)) return localNow
if (!Number.isFinite(offset)) return localNow
return localNow - offset
}