/*
 * Os efeitos da câmera (fundo desfocado e filtros), como no Google Meet: o que
 * a pessoa escolheu e onde fica guardado. Este arquivo é leve de propósito: a
 * tela da live importa só ele; o processamento (WebGL e o modelo que acha a
 * pessoa no quadro) só baixa quando alguém liga um efeito com a câmera aberta.
 */

export type Fundo = "nenhum" | "desfoque";
export type Filtro = "nenhum" | "natural" | "pele" | "iluminado" | "quente" | "rosado";

export type Efeitos = {
  fundo: Fundo;
  /* 0 a 100: quanto o fundo embaça. */
  desfoque: number;
  filtro: Filtro;
  /* 0 a 100: a força do filtro. */
  intensidade: number;
};

export const SEM_EFEITOS: Efeitos = { fundo: "nenhum", desfoque: 60, filtro: "nenhum", intensidade: 60 };

export const FILTROS: Array<{ id: Filtro; nome: string }> = [
  { id: "nenhum", nome: "Nenhum" },
  { id: "natural", nome: "Natural" },
  { id: "pele", nome: "Pele suave" },
  { id: "iluminado", nome: "Iluminado" },
  { id: "quente", nome: "Quente" },
  { id: "rosado", nome: "Rosado" },
];

/*
 * O que cada filtro faz (0 a 1), antes da intensidade. Tudo é ajuste de cor e
 * de textura da pele: nada mexe no formato do rosto.
 */
export type Ajustes = { suavizar: number; iluminar: number; brilho: number; quente: number; rosado: number };

const RECEITAS: Record<Exclude<Filtro, "nenhum">, Ajustes> = {
  natural: { suavizar: 0.55, iluminar: 0.2, brilho: 0.1, quente: 0.08, rosado: 0.1 },
  pele: { suavizar: 1, iluminar: 0.12, brilho: 0.08, quente: 0, rosado: 0.05 },
  iluminado: { suavizar: 0.5, iluminar: 0.6, brilho: 0.55, quente: 0.05, rosado: 0 },
  quente: { suavizar: 0.45, iluminar: 0.2, brilho: 0.1, quente: 0.75, rosado: 0.1 },
  rosado: { suavizar: 0.55, iluminar: 0.2, brilho: 0.15, quente: 0.05, rosado: 0.8 },
};

const NADA: Ajustes = { suavizar: 0, iluminar: 0, brilho: 0, quente: 0, rosado: 0 };

export function ajustesDoFiltro(e: Efeitos): Ajustes {
  if (e.filtro === "nenhum") return NADA;
  const receita = RECEITAS[e.filtro];
  const forca = limitar(e.intensidade) / 100;
  if (!receita || forca <= 0) return NADA;
  return {
    suavizar: receita.suavizar * forca,
    iluminar: receita.iluminar * forca,
    brilho: receita.brilho * forca,
    quente: receita.quente * forca,
    rosado: receita.rosado * forca,
  };
}

/* Algum efeito ligado? Sem nenhum, a câmera vai crua (nada é processado). */
export function temEfeito(e: Efeitos): boolean {
  return querDesfoque(e) || (e.filtro !== "nenhum" && limitar(e.intensidade) > 0);
}

export function querDesfoque(e: Efeitos): boolean {
  return e.fundo === "desfoque" && limitar(e.desfoque) > 0;
}

function limitar(n: number) {
  return Number.isFinite(n) ? Math.min(100, Math.max(0, n)) : 0;
}

/* ------------------------------------------------------------- guardado */

const CHAVE = "ktalogu-live:efeitos";

/* Lê o que ficou salvo neste aparelho (depois de montar: o servidor não tem armazenamento). */
export function lerEfeitos(): Efeitos {
  try {
    const bruto = localStorage.getItem(CHAVE);
    if (!bruto) return SEM_EFEITOS;
    const v = JSON.parse(bruto) as Partial<Efeitos>;
    return {
      fundo: v.fundo === "desfoque" ? "desfoque" : "nenhum",
      desfoque: typeof v.desfoque === "number" ? limitar(v.desfoque) : SEM_EFEITOS.desfoque,
      filtro: FILTROS.some((f) => f.id === v.filtro) ? (v.filtro as Filtro) : "nenhum",
      intensidade: typeof v.intensidade === "number" ? limitar(v.intensidade) : SEM_EFEITOS.intensidade,
    };
  } catch {
    return SEM_EFEITOS;
  }
}

export function salvarEfeitos(e: Efeitos) {
  try {
    if (!temEfeito(e) && e.desfoque === SEM_EFEITOS.desfoque && e.intensidade === SEM_EFEITOS.intensidade) localStorage.removeItem(CHAVE);
    else localStorage.setItem(CHAVE, JSON.stringify(e));
  } catch {
    /* navegador sem armazenamento: só não lembra */
  }
}

/* ------------------------------------------------------------- suporte */

let suporte: boolean | null = null;

/*
 * O navegador consegue processar a câmera? Precisa de WebGL2 e de um jeito de
 * transformar o desenho em trilha de vídeo. Sem isso, os efeitos somem e a
 * câmera segue normal.
 */
export function efeitosPossiveis(): boolean {
  if (suporte !== null) return suporte;
  try {
    const temSaida =
      ("MediaStreamTrackProcessor" in window && "MediaStreamTrackGenerator" in window && "VideoFrame" in window) ||
      typeof HTMLCanvasElement.prototype.captureStream === "function";
    const tela = document.createElement("canvas");
    const gl = tela.getContext("webgl2");
    suporte = Boolean(temSaida && gl);
    (gl?.getExtension("WEBGL_lose_context") as { loseContext?: () => void } | null)?.loseContext?.();
  } catch {
    suporte = false;
  }
  return suporte;
}
