import { beforeEach, describe, expect, it, vi } from "vitest";

import type * as ModuloDeTranscricao from "@/lib/messaging/media/transcription";

/**
 * Visão e transcrição do worker de mídia saíam pagas e não deixavam linha em
 * `llm_calls` — a tabela que a tela de uso, Execuções e o teto de orçamento
 * leem. O que estes casos fixam: chamada que SAIU grava uma linha no ponto
 * certo, com a organização da linha do evento; chamada RECUSADA antes de sair
 * (destino interno, chave da instalação) não grava nada, porque não custou.
 *
 * Mesmo dublê de `midia-base-url-do-binding.test.ts`, mais a captura do insert
 * em `llm_calls`.
 */

const downloadMock = vi.fn();
const llmCallsInsertMock = vi.fn();
const factoryMock = vi.fn(() => "modelo-de-mentira");
const transcribeDoSvcMock = vi.fn(async () => "transcrição de mentira");
const transcribeMedindoDoSvcMock = vi.fn(async () => ({
  texto: "transcrição de mentira", uso: null, custoCents: 0.0508,
}));
const atualizacoesDaMensagem = vi.fn();

const dns = vi.hoisted(() => ({ resposta: [] as Array<{ address: string; family: number }> }));
vi.mock("node:dns/promises", () => {
  const lookup = vi.fn(async () => dns.resposta);
  return { lookup, default: { lookup } };
});

const BINDING = {
  provider: "anthropic",
  model_id: "claude-haiku-4-5",
  credential_id: "cred-1",
  base_url: null as string | null,
};
let bindingDaVez: typeof BINDING | null = BINDING;

let linhaDaMensagem = {
  id: "msg1",
  organization_id: "org1",
  type: "image" as string,
  media_mime: "image/jpeg",
  media_storage_path: "org1/conv1/msg1.jpg",
  media_derived_status: null as string | null,
};

vi.mock("@/lib/supabase/admin", () => ({
  createAdminClient: () => ({
    from: (tabela: string) => {
      const linha =
        tabela === "ai_purpose_bindings" ? bindingDaVez : tabela === "agent_inbox_items" ? null : linhaDaMensagem;
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const terminais: any = {
        maybeSingle: async () => ({ data: linha, error: null }),
        insert: async (row: Record<string, unknown>) => {
          if (tabela === "llm_calls") llmCallsInsertMock(row);
          return { error: null };
        },
        update: (patch: Record<string, unknown>) => {
          if (tabela === "messages") atualizacoesDaMensagem(patch);
          return credencial.limiteConhecido
            ? { eq: () => ({ eq: () => ({ filter: async () => {
                if (credencial.rejeitaUpdate) throw new Error("rejeição sintética de UPDATE");
                return { error: credencial.falhaUpdate ? { message: "falha sintética de UPDATE" } : null };
              } }) }) }
            : { eq: () => ({ eq: async () => ({ error: null }) }) };
        },
      };
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const chain: any = new Proxy(terminais, {
        get: (alvo, prop) => (prop in alvo ? alvo[prop as keyof typeof alvo] : () => chain),
      });
      return chain;
    },
    storage: { from: () => ({ download: downloadMock }) },
  }),
}));

vi.mock("@/lib/messaging/media/derive", () => ({
  deriveMediaText: vi.fn(async () => "derivado de mentira"),
}));

const credencial = vi.hoisted(() => ({
  origemDaChave: "credencial_da_organizacao" as "credencial_da_organizacao" | "chave_da_instalacao",
  /**
   * `null` = a conversa é a Anthropic de sempre e há chave OpenAI (degrau 2).
   * Preenchido = a conversa é este modelo Google, que declara `audio`, e não há
   * chave OpenAI nenhuma — a escada desce ao degrau 4.
   */
  conversaGoogle: null as string | null,
  conversaOpenRouter: false,
  /** O dublê do serviço expõe `transcribeMedindo` com 0,3 centavo (30 s de whisper-1). */
  medirNoDegrau2: false,
  limiteConhecido: false,
  falhaUpdate: false,
  rejeitaUpdate: false,
  params: {} as Record<string, unknown>,
}));
vi.mock("@/lib/agent-engine/edge/llm/credentials", () => ({
  resolveOrgLlmConfig: vi.fn(async (_db: unknown, _cfg: unknown, _org: string, override?: { provider?: string }) => {
    if ((credencial.conversaGoogle || credencial.conversaOpenRouter) && override?.provider === "openai") throw new Error("sem credencial openai");
    return {
      provider: credencial.conversaOpenRouter ? "openrouter" : credencial.conversaGoogle ? "google" : "anthropic",
      apiKey: "chave-do-binding",
      origemDaChave: credencial.origemDaChave,
      defaultModel: credencial.conversaOpenRouter ? "mistralai/modelo-controle" : credencial.conversaGoogle ?? "claude-haiku-4-5",
      params: credencial.params,
      enabledModels: [],
      orcamento: { modo: "off", tetoCents: 0, efetivoEm: null, limiarPct: 80 },
      orcamentoIndisponivelPorque: null,
    };
  }),
}));

vi.mock("@/lib/agent-engine/edge/llm/providers", () => ({
  createDefaultRegistry: () => ({
    anthropic: factoryMock,
    openai: factoryMock,
    openrouter: factoryMock,
    google: factoryMock,
  }),
  // A escada lê estes dois para o degrau OpenRouter (#3382); o mock tem de
  // exportá-los, senão o import do worker morre antes de qualquer caso.
  OPENROUTER_ENDPOINT: "https://openrouter.ai/api/v1",
  cabecalhosDeAtribuicaoOpenRouter: () => undefined,
  fetchDoProvedor: () => globalThis.fetch,
}));

// O catálogo da instalação: controlável por caso, e por padrão "não conhece"
// (null) — o estado de quem não cadastrou preço. Nunca toca um banco.
const catalogo = vi.hoisted(() => ({
  custo: vi.fn(async (): Promise<number | null> => null),
}));
vi.mock("@/lib/agent-engine/edge/llm/pricing-do-catalogo", () => ({
  custoDoModeloForaDoMotor: catalogo.custo,
}));

vi.mock("@/lib/logger", async (importOriginal) => {
  const real = await importOriginal<{ logger: Record<string, unknown> }>();
  return { ...real, logger: { ...real.logger, warn: vi.fn(), info: vi.fn(), error: vi.fn() } };
});

vi.mock("@/lib/ai/pontos/capacidade-em-vigor", () => ({
  visaoEmVigor: vi.fn(async () => ({ enxerga: true, sabemos: true })),
}));

vi.mock("ai", () => ({
  generateText: vi.fn(async () => ({
    text: "descrição de mentira",
    usage: {
      inputTokens: 1200,
      outputTokens: 40,
      inputTokenDetails: { noCacheTokens: 1200, cacheReadTokens: 0, cacheWriteTokens: 0 },
    },
  })),
}));

vi.mock("@/lib/messaging/media/transcription", async (importOriginal) => ({
  ...(await importOriginal<typeof ModuloDeTranscricao>()),
  apiTranscriptionProvider: () => ({
    transcribe: transcribeDoSvcMock,
    ...(credencial.conversaOpenRouter ? { transcribeMedindo: transcribeMedindoDoSvcMock } : {}),
    ...(credencial.medirNoDegrau2
      ? { transcribeMedindo: async () => ({ texto: "transcrição de mentira", uso: null, custoCents: 0.3 }) }
      : {}),
  }),
}));

const transcricaoDoEnv = vi.hoisted(() => ({ apiKey: "", baseUrl: "" }));
vi.mock("@/lib/env", async (importOriginal) => {
  const real = await importOriginal<{ env: Env }>();
  return {
    env: {
      ...real.env,
      get TRANSCRIPTION_API_KEY() {
        return transcricaoDoEnv.apiKey;
      },
      get TRANSCRIPTION_BASE_URL() {
        return transcricaoDoEnv.baseUrl;
      },
      TRANSCRIPTION_MODEL: "",
      TRANSCRIPTION_LANGUAGES: "",
      IA_DESTINOS_INTERNOS_PERMITIDOS: "",
    },
  };
});

import { deriveMessageMedia } from "@/workers/media-derive-worker";
import { esquecerDestinosInternos } from "@/lib/automation/destinos-internos-autorizados";
import { PONTO_TRANSCRICAO_DE_AUDIO, PONTO_VISAO_DE_IMAGEM } from "@/lib/ai/pontos/registro";
import { deriveMediaText, type DeriveDeps } from "@/lib/messaging/media/derive";
import type { Env } from "@/lib/env";
import { TranscriptionTooLargeError } from "@/lib/messaging/media/transcription";
import { logger } from "@/lib/logger";
import { TETO_DE_SAIDA_PADRAO } from "@/lib/agent-engine/edge/llm/run-model-call";

function eventRow() {
  return {
    id: "ev1",
    organization_id: "org1",
    event_type: "media.derive_requested",
    entity_kind: "message",
    entity_id: "msg1",
    payload: { message_id: "msg1" },
    metadata: {},
    consumed_by: [],
    attempts: 0,
  };
}

function depsDaChamada(): DeriveDeps {
  const deps = vi.mocked(deriveMediaText).mock.calls[0]?.[3];
  if (!deps) throw new Error("deriveMediaText não recebeu deps: a derivação parou antes");
  return deps;
}

function comAudio(): void {
  linhaDaMensagem = { ...linhaDaMensagem, type: "audio", media_mime: "audio/ogg", media_storage_path: "org1/conv1/msg1.ogg" };
}

beforeEach(() => {
  vi.clearAllMocks();
  esquecerDestinosInternos();
  dns.resposta = [{ address: "93.184.216.34", family: 4 }];
  credencial.origemDaChave = "credencial_da_organizacao";
  credencial.conversaGoogle = null;
  credencial.conversaOpenRouter = false;
  credencial.medirNoDegrau2 = false;
  credencial.limiteConhecido = false;
  credencial.falhaUpdate = false;
  credencial.rejeitaUpdate = false;
  credencial.params = {};
  bindingDaVez = BINDING;
  transcricaoDoEnv.apiKey = "";
  transcricaoDoEnv.baseUrl = "";
  linhaDaMensagem = {
    id: "msg1",
    organization_id: "org1",
    type: "image",
    media_mime: "image/jpeg",
    media_storage_path: "org1/conv1/msg1.jpg",
    media_derived_status: null,
  };
  downloadMock.mockResolvedValue({ data: { arrayBuffer: async () => new ArrayBuffer(8) }, error: null });
  catalogo.custo.mockImplementation(async () => null);
});

describe("worker de mídia: chamada paga grava em llm_calls", () => {
  it("visão que saiu grava uma linha no ponto da visão, com tokens e custo da tabela de preços", async () => {
    await deriveMessageMedia(eventRow());
    await depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg");

    expect(factoryMock).toHaveBeenCalled();
    expect(llmCallsInsertMock).toHaveBeenCalledTimes(1);
    const linha = llmCallsInsertMock.mock.calls[0]![0] as Record<string, unknown>;
    expect(linha).toMatchObject({
      organization_id: "org1",
      purpose: PONTO_VISAO_DE_IMAGEM,
      provider: "anthropic",
      model: "claude-haiku-4-5",
      input_tokens: 1200,
      output_tokens: 40,
      status: "ok",
    });
    // haiku-4-5: 1 USD/MTok de entrada, 5 de saída → (1200×1 + 40×5)/1e6 USD
    expect(linha.cost_cents).toBeCloseTo(((1200 * 1 + 40 * 5) / 1_000_000) * 100, 10);
    expect(typeof linha.latency_ms).toBe("number");
  });

  it("visão sai com teto de saída: sem ele a OpenRouter reserva o máximo do modelo contra o saldo", async () => {
    const { generateText } = await import("ai");
    await deriveMessageMedia(eventRow());
    await depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg");
    const pedido = vi.mocked(generateText).mock.calls[0]?.[0] as { maxOutputTokens?: number } | undefined;
    expect(pedido?.maxOutputTokens).toBe(TETO_DE_SAIDA_PADRAO);
  });

  it("visão respeita o teto que a organização subiu (modelo de raciocínio), como as respostas do agente", async () => {
    const { generateText } = await import("ai");
    credencial.params = { maxOutputTokens: 32000 };
    await deriveMessageMedia(eventRow());
    await depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg");
    const pedido = vi.mocked(generateText).mock.calls[0]?.[0] as { maxOutputTokens?: number } | undefined;
    expect(pedido?.maxOutputTokens).toBe(32000);
  });

  it("teto da organização com forma errada vale como ausente: a visão sai com o padrão", async () => {
    const { generateText } = await import("ai");
    credencial.params = { maxOutputTokens: "muito" };
    await deriveMessageMedia(eventRow());
    await depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg");
    const pedido = vi.mocked(generateText).mock.calls[0]?.[0] as { maxOutputTokens?: number } | undefined;
    expect(pedido?.maxOutputTokens).toBe(TETO_DE_SAIDA_PADRAO);
  });

  it("visão recusada por destino interno não grava linha", async () => {
    bindingDaVez = { ...BINDING, base_url: "http://169.254.169.254/v1" };

    await deriveMessageMedia(eventRow());
    await depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg");

    expect(factoryMock).not.toHaveBeenCalled();
    expect(llmCallsInsertMock).not.toHaveBeenCalled();
  });

  it("visão recusada por endereço da empresa com chave da instalação não grava linha", async () => {
    credencial.origemDaChave = "chave_da_instalacao";
    bindingDaVez = { ...BINDING, base_url: "https://gateway.publico.exemplo/v1" };

    await deriveMessageMedia(eventRow());
    await depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg");

    expect(factoryMock).not.toHaveBeenCalled();
    expect(llmCallsInsertMock).not.toHaveBeenCalled();
  });

  it("transcrição que saiu grava uma linha no ponto do áudio, com custo desconhecido (null, nunca 0)", async () => {
    comAudio();

    await deriveMessageMedia(eventRow());
    await depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg");

    expect(transcribeDoSvcMock).toHaveBeenCalled();
    expect(llmCallsInsertMock).toHaveBeenCalledTimes(1);
    expect(llmCallsInsertMock.mock.calls[0]![0]).toMatchObject({
      organization_id: "org1",
      purpose: PONTO_TRANSCRICAO_DE_AUDIO,
      provider: "openai",
      model: "whisper-1",
      input_tokens: 0,
      output_tokens: 0,
      cost_cents: null,
      status: "ok",
    });
  });

  it("fiação: o custo que o serviço mede (duração cobrada pela OpenAI) chega à linha do degrau 2", async () => {
    comAudio();
    credencial.medirNoDegrau2 = true;

    await deriveMessageMedia(eventRow());
    await depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg");

    expect(llmCallsInsertMock).toHaveBeenCalledTimes(1);
    expect(llmCallsInsertMock.mock.calls[0]![0]).toMatchObject({
      origem_da_escolha: "padrao_openai_compativel",
      model: "whisper-1",
      cost_cents: 0.3,
    });
  });

  it("transcrição pelo serviço grava o degrau da escada, que é o que a separa do furo de medição", async () => {
    comAudio();

    await deriveMessageMedia(eventRow());
    await depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg");

    expect(llmCallsInsertMock.mock.calls[0]![0]).toMatchObject({ origem_da_escolha: "padrao_openai_compativel" });
  });

  it("persiste o custo medido pelo serviço OpenRouter sem estimar preço por tokens", async () => {
    comAudio();
    credencial.conversaOpenRouter = true;
    bindingDaVez = null;

    await deriveMessageMedia(eventRow());
    await depsDaChamada().transcriber.transcribe(Buffer.from("controle"), "audio/ogg");

    expect(transcribeMedindoDoSvcMock).toHaveBeenCalledTimes(1);
    expect(llmCallsInsertMock).toHaveBeenCalledTimes(1);
    expect(llmCallsInsertMock.mock.calls[0]![0]).toMatchObject({
      organization_id: "org1",
      provider: "openrouter",
      origem_da_escolha: "padrao_openrouter",
      input_tokens: 0,
      output_tokens: 0,
      cost_cents: 0.0508,
    });
  });

  it("o limite conhecido termina em failed já na primeira tentativa, sem chamada de custo", async () => {
    comAudio();
    credencial.conversaOpenRouter = true;
    credencial.limiteConhecido = true;
    bindingDaVez = null;
    // O tamanho/zero fetch são medidos no contrato do provider. Aqui o dublê
    // devolve a recusa tipada para medir o tratamento pelo worker real.
    transcribeMedindoDoSvcMock.mockRejectedValueOnce(new TranscriptionTooLargeError(25_000_000));
    vi.mocked(deriveMediaText).mockImplementationOnce(async (_tipo, audio, mime, deps) =>
      deps.transcriber.transcribe(audio, mime));

    const resultado = await deriveMessageMedia(eventRow());

    expect(resultado).toMatchObject({ status: "skipped", detail: "transcription_audio_too_large" });
    expect(atualizacoesDaMensagem).toHaveBeenCalledWith(expect.objectContaining({
      media_derived_status: "failed",
      metadata: expect.objectContaining({ media_derived_motivo: expect.stringContaining("25 MB") }),
    }));
    expect(llmCallsInsertMock).not.toHaveBeenCalled();
  });

  it.each(["erro", "rejeicao"])("não declara o limite terminado quando o UPDATE de failed devolve %s", async (modo) => {
    comAudio();
    credencial.conversaOpenRouter = true;
    credencial.limiteConhecido = true;
    credencial.falhaUpdate = true;
    credencial.rejeitaUpdate = modo === "rejeicao";
    bindingDaVez = null;
    transcribeMedindoDoSvcMock.mockRejectedValueOnce(new TranscriptionTooLargeError(25_000_000));
    vi.mocked(deriveMediaText).mockImplementationOnce(async (_tipo, audio, mime, deps) =>
      deps.transcriber.transcribe(audio, mime));

    const resultado = await deriveMessageMedia(eventRow());

    expect(resultado.status).toBe("error");
    expect(llmCallsInsertMock).not.toHaveBeenCalled();
  });

  it("transcrição pelo modelo da organização (degrau 4) grava tokens e o custo da tabela de preços", async () => {
    comAudio();
    credencial.conversaGoogle = "gemini-2.5-flash";
    bindingDaVez = null;

    await deriveMessageMedia(eventRow());
    const texto = await depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg");

    // O áudio foi ao MODELO, não ao serviço de transcrição.
    expect(transcribeDoSvcMock).not.toHaveBeenCalled();
    expect(texto).toBe("descrição de mentira");
    expect(llmCallsInsertMock).toHaveBeenCalledTimes(1);
    const linha = llmCallsInsertMock.mock.calls[0]![0] as Record<string, unknown>;
    expect(linha).toMatchObject({
      organization_id: "org1",
      purpose: PONTO_TRANSCRICAO_DE_AUDIO,
      provider: "google",
      model: "gemini-2.5-flash",
      origem_da_escolha: "modelo_da_organizacao",
      input_tokens: 1200,
      output_tokens: 40,
      status: "ok",
    });
    // gemini-2.5-flash: 0,30 USD/MTok de entrada, 2,50 de saída
    expect(linha.cost_cents).toBeCloseTo(((1200 * 0.3 + 40 * 2.5) / 1_000_000) * 100, 10);
  });

  it("degrau 4 com modelo fora da tabela de preços grava os tokens e custo null — nunca 0", async () => {
    comAudio();
    credencial.conversaGoogle = "gemini-modelo-que-ninguem-precificou";
    bindingDaVez = null;

    await deriveMessageMedia(eventRow());
    await depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg");

    // A consulta ao catálogo corre DEPOIS de o texto voltar: a linha chega em seguida.
    await vi.waitFor(() => expect(llmCallsInsertMock).toHaveBeenCalledTimes(1));
    expect(llmCallsInsertMock.mock.calls[0]![0]).toMatchObject({
      origem_da_escolha: "modelo_da_organizacao",
      input_tokens: 1200,
      cost_cents: null,
    });
  });

  it("degrau 4 com modelo fora da tabela, mas no catálogo da instalação, grava o custo do catálogo", async () => {
    comAudio();
    credencial.conversaGoogle = "gemini-modelo-que-ninguem-precificou";
    bindingDaVez = null;
    catalogo.custo.mockImplementation(async () => 2.5);

    await deriveMessageMedia(eventRow());
    await depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg");

    await vi.waitFor(() => expect(llmCallsInsertMock).toHaveBeenCalledTimes(1));
    expect(llmCallsInsertMock.mock.calls[0]![0]).toMatchObject({
      origem_da_escolha: "modelo_da_organizacao",
      model: "gemini-modelo-que-ninguem-precificou",
      cost_cents: 2.5,
    });
    expect(catalogo.custo).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
      model: "gemini-modelo-que-ninguem-precificou",
      provider: "google",
    }));
  });

  it("visão com modelo fora da tabela consulta o catálogo, como o seam", async () => {
    credencial.conversaGoogle = "gemini-modelo-que-ninguem-precificou";
    bindingDaVez = null;
    catalogo.custo.mockImplementation(async () => 1.23);

    await deriveMessageMedia(eventRow());
    await depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg");

    await vi.waitFor(() => expect(llmCallsInsertMock).toHaveBeenCalledTimes(1));
    expect(llmCallsInsertMock.mock.calls[0]![0]).toMatchObject({
      purpose: PONTO_VISAO_DE_IMAGEM,
      model: "gemini-modelo-que-ninguem-precificou",
      cost_cents: 1.23,
    });
  });

  it("guarda: catálogo que nunca responde não segura o texto da visão nem o do degrau 4", async () => {
    credencial.conversaGoogle = "gemini-modelo-que-ninguem-precificou";
    bindingDaVez = null;
    catalogo.custo.mockImplementation(() => new Promise<number | null>(() => {}));

    await deriveMessageMedia(eventRow());
    await expect(depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg")).resolves.toBe("descrição de mentira");

    vi.mocked(deriveMediaText).mockClear();
    comAudio();
    await deriveMessageMedia(eventRow());
    await expect(depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg")).resolves.toBe(
      "descrição de mentira",
    );
    expect(llmCallsInsertMock).not.toHaveBeenCalled();
  });

  it("catálogo que rejeita: o texto volta e sai o aviso de montagem, sem linha", async () => {
    credencial.conversaGoogle = "gemini-modelo-que-ninguem-precificou";
    bindingDaVez = null;
    catalogo.custo.mockImplementation(async () => {
      throw new Error("catálogo fora do ar");
    });

    await deriveMessageMedia(eventRow());
    await expect(depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg")).resolves.toBe("descrição de mentira");

    await vi.waitFor(() =>
      expect(vi.mocked(logger.warn)).toHaveBeenCalledWith(
        "[media-derive] não consegui montar a linha de custo",
        expect.objectContaining({ organization_id: "org1", error: "catálogo fora do ar" }),
      ),
    );
    expect(llmCallsInsertMock).not.toHaveBeenCalled();
  });

  it("transcrição recusada por destino interno não grava linha", async () => {
    comAudio();
    transcricaoDoEnv.apiKey = "chave-do-servico";
    transcricaoDoEnv.baseUrl = "http://169.254.169.254/v1";

    await deriveMessageMedia(eventRow());
    await depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg");

    expect(transcribeDoSvcMock).not.toHaveBeenCalled();
    expect(llmCallsInsertMock).not.toHaveBeenCalled();
  });

  it("transcrição que falha no provedor grava a linha de erro e o erro continua subindo", async () => {
    comAudio();
    transcribeDoSvcMock.mockRejectedValueOnce(new Error("transcription_401"));

    await deriveMessageMedia(eventRow());
    await expect(depsDaChamada().transcriber.transcribe(Buffer.from("ogg"), "audio/ogg")).rejects.toThrow(
      "transcription_401",
    );

    expect(llmCallsInsertMock).toHaveBeenCalledTimes(1);
    const linha = llmCallsInsertMock.mock.calls[0]![0] as Record<string, unknown>;
    expect(linha).toMatchObject({
      organization_id: "org1",
      purpose: PONTO_TRANSCRICAO_DE_AUDIO,
      status: "erro",
      cost_cents: null,
      input_tokens: 0,
    });
    expect(String(linha.error_message)).toContain("transcription_401");
    expect(typeof linha.error_code).toBe("string");
  });

  it("visão que falha no provedor grava a linha de erro e o erro continua subindo", async () => {
    const { generateText } = await import("ai");
    vi.mocked(generateText).mockRejectedValueOnce(Object.assign(new Error("invalid x-api-key"), { statusCode: 401 }));

    await deriveMessageMedia(eventRow());
    await expect(depsDaChamada().describeImage(Buffer.from("jpeg"), "image/jpeg")).rejects.toThrow("invalid x-api-key");

    expect(llmCallsInsertMock).toHaveBeenCalledTimes(1);
    expect(llmCallsInsertMock.mock.calls[0]![0]).toMatchObject({
      organization_id: "org1",
      purpose: PONTO_VISAO_DE_IMAGEM,
      status: "erro",
      cost_cents: null,
      http_status: 401,
    });
  });
});

describe("as constantes de purpose da mídia casam com o registro de pontos", () => {
  it("cada uma é o id de uma entrada — senão a linha em llm_calls fica sem rótulo em Execuções", async () => {
    const { PONTO_POR_ID } = await import("@/lib/ai/pontos/registro");
    expect(PONTO_POR_ID.get(PONTO_TRANSCRICAO_DE_AUDIO)?.registraEm).toBe("llm_calls");
    expect(PONTO_POR_ID.get(PONTO_VISAO_DE_IMAGEM)).toBeDefined();
  });
});
