"use client";

import type { ImageSegmenter } from "@mediapipe/tasks-vision";

/*
 * Quem está no quadro: o MediaPipe (do Google, o mesmo do Meet) roda no
 * navegador e devolve, para cada ponto da imagem, a chance de ser a pessoa.
 * Os arquivos ficam na própria loja (public/live-efeitos): nada de CDN de
 * fora na hora da live. O wasm é copiado do node_modules no build
 * (scripts/copiar-efeitos-da-camera.mjs); o modelo vai no repositório.
 *
 * Um só por aba: trocar de câmera, sair e entrar de novo usa o mesmo.
 */

const BASE = "/live-efeitos";
/* O modelo "paisagem" (144x256) é o mais leve, feito para a câmera deitada. */
const MODELO = `${BASE}/selfie_segmenter_landscape.tflite`;

export type Segmentador = {
  /* Roda num quadro e devolve a máscara (0 a 255, 1 byte por ponto), ou null se falhou. */
  segmentar: (quadro: TexImageSource, agoraMs: number) => { dados: Uint8Array; largura: number; altura: number } | null;
  usaGpu: boolean;
};

let promessa: Promise<Segmentador> | null = null;

export function carregarSegmentador(): Promise<Segmentador> {
  if (!promessa) {
    promessa = criar().catch((erro) => {
      /* Falhou (rede, navegador): a próxima tentativa começa do zero. */
      promessa = null;
      throw erro;
    });
  }
  return promessa;
}

async function criar(): Promise<Segmentador> {
  const { FilesetResolver, ImageSegmenter: Segmenter } = await import("@mediapipe/tasks-vision");
  const simd = await FilesetResolver.isSimdSupported().catch(() => false);
  const nome = simd ? "vision_wasm_internal" : "vision_wasm_nosimd_internal";
  const arquivos = { wasmLoaderPath: `${BASE}/wasm/${nome}.js`, wasmBinaryPath: `${BASE}/wasm/${nome}.wasm` };

  const abrir = (delegate: "GPU" | "CPU") =>
    Segmenter.createFromOptions(arquivos, {
      baseOptions: { modelAssetPath: MODELO, delegate },
      runningMode: "VIDEO",
      outputConfidenceMasks: true,
      outputCategoryMask: false,
      /* Uma tela própria para o MediaPipe: não disputa o WebGL do desenho. */
      ...(delegate === "GPU" ? { canvas: novaTela() } : {}),
    });

  let segmenter: ImageSegmenter;
  let usaGpu = true;
  try {
    segmenter = await abrir("GPU");
  } catch {
    usaGpu = false;
    segmenter = await abrir("CPU");
  }

  let ultimo = 0;
  let saida: Uint8Array | null = null;
  return {
    usaGpu,
    segmentar(quadro, agoraMs) {
      /* O MediaPipe exige o relógio sempre andando para a frente. */
      const ts = Math.max(agoraMs, ultimo + 1);
      ultimo = ts;
      let resultado: { dados: Uint8Array; largura: number; altura: number } | null = null;
      try {
        segmenter.segmentForVideo(quadro, ts, (r) => {
          const mascaras = r.confidenceMasks;
          if (!mascaras?.length) return;
          /* Com mais de uma máscara, a última é a pessoa; com uma só, ela já é a pessoa. */
          const m = mascaras[mascaras.length - 1];
          const f = m.getAsFloat32Array();
          if (!saida || saida.length !== f.length) saida = new Uint8Array(f.length);
          for (let i = 0; i < f.length; i++) saida[i] = f[i] <= 0 ? 0 : f[i] >= 1 ? 255 : (f[i] * 255) | 0;
          resultado = { dados: saida, largura: m.width, altura: m.height };
        });
      } catch {
        return null;
      }
      return resultado;
    },
  };
}

function novaTela(): HTMLCanvasElement | OffscreenCanvas {
  if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(1, 1);
  return document.createElement("canvas");
}
