import type { ChatMediaKind } from "@/data/schemas/chat";
import { normalizeLegacyCharacterId } from "@/data/constants/character_id_aliases";

const CHARACTER_NAMESPACE = "::character:";
const LEGACY_OWNER_KEY_PATTERN = /^(?:user|device):.+$/u;
const LEGACY_DEFAULT_CHARACTER_ID = "elio";

export function buildChatConversationKey(
  ownerKey: string,
  characterId: string,
): string {
  if (ownerKey.trim().length === 0 || characterId.trim().length === 0) {
    throw new Error("Chat owner and character identities must not be empty.");
  }
  return `${ownerKey}${CHARACTER_NAMESPACE}${encodeURIComponent(characterId)}`;
}

export function buildChatMediaCacheKey(input: {
  ownerKey: string;
  messageId: string;
  kind: ChatMediaKind;
}): string {
  return `${input.ownerKey}:${input.messageId}:${input.kind}`;
}

/**
 * Migrates both pre-character owner keys and keys containing the first
 * frontend-only character IDs. This is intentionally idempotent so it can be
 * used by database upgrades and compatibility reads.
 */
export function migrateLegacyChatConversationKey(ownerKey: string): string {
  const namespaceIndex = ownerKey.lastIndexOf(CHARACTER_NAMESPACE);
  if (namespaceIndex < 0) {
    return LEGACY_OWNER_KEY_PATTERN.test(ownerKey)
      ? buildChatConversationKey(ownerKey, LEGACY_DEFAULT_CHARACTER_ID)
      : ownerKey;
  }

  const encodedCharacterId = ownerKey.slice(
    namespaceIndex + CHARACTER_NAMESPACE.length,
  );
  if (encodedCharacterId.length === 0) return ownerKey;

  let characterId: string;
  try {
    characterId = decodeURIComponent(encodedCharacterId);
  } catch {
    return ownerKey;
  }

  const normalizedCharacterId = normalizeLegacyCharacterId(characterId);
  if (normalizedCharacterId === characterId) return ownerKey;

  return buildChatConversationKey(
    ownerKey.slice(0, namespaceIndex),
    normalizedCharacterId,
  );
}
