/**
 * Presence and typing state, all in Redis so it is correct across instances.
 *
 * Presence is derived from live socket counts rather than a self-reported flag:
 * an agent is online while at least one of their sockets is connected, which
 * survives a browser crash without a stale "online" badge.
 */
import { redis, KEYS } from '../redis/client.js';
import { env } from '../config/env.js';
import type { Presence } from '../modules/types.js';

/* ---------------- agent sockets / presence ---------------- */

export async function addAgentSocket(agentId: string, socketId: string): Promise<number> {
  const key = KEYS.agentSockets(agentId);
  await redis.sadd(key, socketId);
  await redis.expire(key, 60 * 60 * 24);
  return redis.scard(key);
}

export async function removeAgentSocket(agentId: string, socketId: string): Promise<number> {
  const key = KEYS.agentSockets(agentId);
  await redis.srem(key, socketId);
  return redis.scard(key);
}

export async function isAgentOnline(agentId: string): Promise<boolean> {
  return (await redis.scard(KEYS.agentSockets(agentId))) > 0;
}

export async function setAgentPresence(agentId: string, presence: Presence): Promise<void> {
  if (presence === 'offline') await redis.hdel(KEYS.agentPresence, agentId);
  else await redis.hset(KEYS.agentPresence, agentId, presence);
}

export async function allAgentPresence(): Promise<Record<string, Presence>> {
  return (await redis.hgetall(KEYS.agentPresence)) as Record<string, Presence>;
}

/* ---------------- conversation sockets ---------------- */

export async function addConversationSocket(conversationId: string, socketId: string): Promise<void> {
  const key = KEYS.convSockets(conversationId);
  await redis.sadd(key, socketId);
  await redis.expire(key, 60 * 60 * 24);
}

export async function removeConversationSocket(conversationId: string, socketId: string): Promise<void> {
  await redis.srem(KEYS.convSockets(conversationId), socketId);
}

/** Is the customer currently connected to this conversation? */
export async function conversationHasSockets(conversationId: string): Promise<boolean> {
  return (await redis.scard(KEYS.convSockets(conversationId))) > 0;
}

/* ---------------- typing ---------------- */

/**
 * Typing state auto-expires, so a client that closes its tab mid-sentence
 * cannot leave a stuck "is typing..." indicator on the other side.
 */
export async function setTyping(
  conversationId: string,
  actorId: string,
  isTyping: boolean,
): Promise<void> {
  const key = KEYS.typing(conversationId);
  if (isTyping) {
    await redis.hset(key, actorId, Date.now());
    await redis.pexpire(key, env.TYPING_TIMEOUT_MS * 3);
  } else {
    await redis.hdel(key, actorId);
  }
}

export async function whoIsTyping(conversationId: string): Promise<string[]> {
  const entries = await redis.hgetall(KEYS.typing(conversationId));
  const cutoff = Date.now() - env.TYPING_TIMEOUT_MS;
  return Object.entries(entries)
    .filter(([, ts]) => Number(ts) > cutoff)
    .map(([actorId]) => actorId);
}
