import { NextResponse } from "next/server";
import { acessoDoChat } from "@/lib/chats/acesso";
import { avisarConversa, canalDaConversa, canalDaPessoa, canalDoCliente } from "@/lib/chats/aviso";
import {
  avisarClientePorPush,
  avisarVisitantePorPush,
  enderecoDaLoja,
  enderecoDoPortal,
} from "@/lib/chats/push";
import {
  BUCKET_PRIVADO_DA_CONVERSA,
  TAMANHO_MAXIMO_DO_ANEXO,
  apagarArquivoDaMensagem,
  atualizarResumoDaConversa,
  listarMensagensDaConversa,
  marcarConversaComoLida,
  mensagemParaTela,
  urlDoAvatar,
} from "@/lib/chats/servidor";
import { createAdminClient } from "@/lib/supabase-server";

/*
  Mensagens de uma conversa, vistas de dentro do Chats.

  A mesma conversa do pedido tem duas portas: a aba do pedido e esta. Por isso o
  que vale aqui e o fio, e nao o assunto — quem manda por uma porta aparece na
  outra na hora.
*/

/* Quantas mensagens antigas vêm a cada "carregar mais". */
const PAGINA_DE_MENSAGENS = 60;

function texto(valor: unknown) {
  return String(valor ?? "").trim();
}

type MensagemNaTela = ReturnType<typeof mensagemParaTela> & {
  respondendo?: { id: string; quem: string; resumo: string } | null;
};

/*
  A mensagem citada, resolvida aqui.

  Quem responde citando espera ver o trecho citado no balão, como em qualquer
  aplicativo de mensagem. Resolver isso na tela exigiria a mensagem antiga estar
  carregada — e depois de um "carregar mais" ela quase nunca está.
*/
function comAsCitacoes(lista: MensagemNaTela[]) {
  const porId = new Map(lista.map((mensagem) => [mensagem.id, mensagem]));

  return lista.map((mensagem) => {
    if (!mensagem.reply_to) return mensagem;
    const citada = porId.get(mensagem.reply_to);
    if (!citada) return { ...mensagem, respondendo: null };

    return {
      ...mensagem,
      respondendo: {
        id: citada.id,
        quem: citada.sender === "client" ? citada.sender_name || "Cliente" : citada.sender_name || "Você",
        resumo:
          texto(citada.body).slice(0, 120) ||
          (citada.attachment_kind === "image" ? "Imagem" : citada.attachment_kind ? "Arquivo" : "Mensagem"),
      },
    };
  });
}

type Conversa = {
  id: string;
  user_id: string;
  kind: string;
  order_id: string | null;
  quote_id: string | null;
  visitor_name: string | null;
  visitor_contact: string | null;
  closed_at: string | null;
  closed_by: string | null;
  sessions_count: number | null;
  first_seen_at: string | null;
  last_seen_at: string | null;
  client_id: string | null;
  member_a: string | null;
  member_b: string | null;
  title: string | null;
  unread_store: number | null;
  unread_other: number | null;
  visitor_key: string | null;
  stage: string | null;
  assigned_to: string | null;
  assigned_name: string | null;
  tags: string[] | null;
  follow_up_at: string | null;
  follow_up_note: string | null;
};

/*
  Quem pode abrir esta conversa — e de que lado esta sentado.

  Na conversa da equipe nao existe "loja" e "cliente": sao duas pessoas iguais.
  A dupla ja e guardada em ordem fixa, entao o primeiro da dupla usa o lado da
  loja e o segundo usa o outro lado. E so uma convencao para as confirmacoes de
  leitura terem onde morar.
*/
async function abrirConversa(threadId: string, acesso: Awaited<ReturnType<typeof acessoDoChat>>) {
  if ("erro" in acesso) return { erro: acesso.erro } as const;

  const admin = createAdminClient();
  const { data } = await admin
    .from("chat_threads")
    .select(
      "id,user_id,kind,order_id,quote_id,client_id,member_a,member_b,title,visitor_name,visitor_contact,visitor_key,stage,assigned_to,assigned_name,tags,follow_up_at,follow_up_note,closed_at,closed_by,sessions_count,first_seen_at,last_seen_at,unread_store,unread_other"
    )
    .eq("id", threadId)
    .maybeSingle();

  const conversa = data as Conversa | null;
  if (!conversa?.id) {
    return { erro: NextResponse.json({ error: "Conversa nao encontrada." }, { status: 404 }) } as const;
  }

  if (conversa.kind === "team") {
    const participa = conversa.member_a === acesso.user.id || conversa.member_b === acesso.user.id;
    if (!participa) {
      return { erro: NextResponse.json({ error: "Sem permissao." }, { status: 403 }) } as const;
    }
    return {
      admin,
      conversa,
      lado: conversa.member_a === acesso.user.id ? ("store" as const) : ("other" as const),
      autor: "team" as const,
      podeResponder: true,
    } as const;
  }

  const daMinhaLoja = acesso.tenantIds.includes(conversa.user_id);
  if (!daMinhaLoja || !acesso.podeVerLoja) {
    return { erro: NextResponse.json({ error: "Sem permissao." }, { status: 403 }) } as const;
  }

  return {
    admin,
    conversa,
    lado: "store" as const,
    autor: "store" as const,
    podeResponder: acesso.podeResponderLoja,
  } as const;
}

export async function GET(req: Request, context: { params: Promise<{ threadId: string }> }) {
  const { threadId } = await context.params;
  const acesso = await acessoDoChat();
  if ("erro" in acesso) return acesso.erro;

  const aberta = await abrirConversa(threadId, acesso);
  if ("erro" in aberta) return aberta.erro;

  /* A releitura de dez em dez segundos so quer as mensagens. Refazer o
     cabecalho (cliente, pedido, foto assinada) a cada volta era trabalho jogado
     fora — e era ele que fazia a conversa parecer lenta. */
  const { searchParams } = new URL(req.url);
  const soAsMensagens = searchParams.get("so") === "mensagens";

  /*
    Conversa que não corta.

    A tela abre com o pedaço recente, que é o que interessa; pedindo "antes de
    tal hora" vem o anterior. Antes disso, uma conversa antiga simplesmente
    perdia o começo — e o começo é onde costuma estar o combinado.
  */
  const antes = texto(searchParams.get("antes"));
  const mensagens = antes
    ? await listarMensagensDaConversa(aberta.admin, threadId, PAGINA_DE_MENSAGENS, antes)
    : await listarMensagensDaConversa(aberta.admin, threadId);

  if (antes) {
    return NextResponse.json({
      ok: true,
      meuId: acesso.user.id,
      mensagens: comAsCitacoes(mensagens.map((mensagem) => mensagemParaTela(mensagem, aberta.lado))),
      temMais: mensagens.length >= PAGINA_DE_MENSAGENS,
    });
  }

  const naoLidas = Number(
    (aberta.lado === "store" ? aberta.conversa.unread_store : aberta.conversa.unread_other) || 0
  );
  const temNaoLidaNaTela = mensagens.some((mensagem) =>
    aberta.lado === "store" ? !mensagem.read_by_store_at : !mensagem.read_by_other_at
  );
  if (naoLidas > 0 || temNaoLidaNaTela) {
    await marcarConversaComoLida(
      aberta.admin,
      threadId,
      aberta.lado,
      aberta.conversa.kind === "team",
      acesso.user.id
    );
  }

  if (soAsMensagens) {
    return NextResponse.json({
      ok: true,
      meuId: acesso.user.id,
      mensagens: comAsCitacoes(mensagens.map((mensagem) => mensagemParaTela(mensagem, aberta.lado))),
    });
  }

  /* O cabecalho da conversa: nome de quem esta do outro lado e o vinculo. */
  let contato = aberta.conversa.title || "Conversa";
  let detalhe = "";
  let avatar: string | null = null;

  if (aberta.conversa.kind === "visitor") {
    /* Visitante nao tem ficha: o nome e o contato sao o que ele mesmo digitou. */
    contato = texto(aberta.conversa.visitor_name) || "Visitante da loja";
    detalhe = texto(aberta.conversa.visitor_contact) || "Visitante da loja online";
  } else if (aberta.conversa.kind === "team") {
    const outro = aberta.conversa.member_a === acesso.user.id ? aberta.conversa.member_b : aberta.conversa.member_a;
    const { data: colega } = await aberta.admin
      .from("company_users")
      .select("email,role")
      .eq("user_id", outro || "")
      .maybeSingle();
    contato = texto(colega?.email).split("@")[0] || contato;
    detalhe = texto(colega?.email);
  } else if (aberta.conversa.client_id) {
    const { data: cliente } = await aberta.admin
      .from("clients")
      .select("name,company_name,avatar_url,phone,email")
      .eq("id", aberta.conversa.client_id)
      .maybeSingle();
    contato = texto(cliente?.name) || texto(cliente?.company_name) || contato;
    avatar = await urlDoAvatar(aberta.admin, cliente?.avatar_url);
    detalhe = texto(cliente?.phone) || texto(cliente?.email);
  }

  let pedido: {
    id: string;
    number: number;
    status: string | null;
    total_amount: number | null;
    art_status: string | null;
  } | null = null;
  if (aberta.conversa.order_id) {
    const { data } = await aberta.admin
      .from("orders")
      .select("id,number,status,total_amount,art_status")
      .eq("id", aberta.conversa.order_id)
      .maybeSingle();
    pedido = (data as typeof pedido) || null;
  }

  let orcamento: { id: string; number: number; status: string | null; total: number | null } | null = null;
  if (aberta.conversa.quote_id) {
    const { data } = await aberta.admin
      .from("quotes")
      .select("id,number,status,total")
      .eq("id", aberta.conversa.quote_id)
      .maybeSingle();
    orcamento = (data as typeof orcamento) || null;
  }

  return NextResponse.json({
    ok: true,
    meuId: acesso.user.id,
    conversa: {
      id: aberta.conversa.id,
      kind: aberta.conversa.kind,
      contato,
      detalhe,
      avatar,
      orderId: aberta.conversa.order_id,
      quoteId: aberta.conversa.quote_id,
      clientId: aberta.conversa.client_id,
      pedido,
      orcamento,
      podeResponder: aberta.podeResponder,
      souOLadoDaLoja: aberta.lado === "store",
      encerrada: Boolean(aberta.conversa.closed_at),
      encerradaPor: aberta.conversa.closed_by,
      encerradaEm: aberta.conversa.closed_at,
      visitas: Number(aberta.conversa.sessions_count || 1),
      primeiraVisita: aberta.conversa.first_seen_at,
      ultimaVisita: aberta.conversa.last_seen_at,
      chaveDoVisitante: aberta.conversa.visitor_key,
      estagio: aberta.conversa.stage,
      responsavel: aberta.conversa.assigned_to,
      responsavelNome: aberta.conversa.assigned_name,
      etiquetas: Array.isArray(aberta.conversa.tags) ? aberta.conversa.tags : [],
      retomarEm: aberta.conversa.follow_up_at,
      retomarNota: aberta.conversa.follow_up_note,
    },
    mensagens: comAsCitacoes(mensagens.map((mensagem) => mensagemParaTela(mensagem, aberta.lado))),
  });
}

export async function POST(req: Request, context: { params: Promise<{ threadId: string }> }) {
  const { threadId } = await context.params;
  const acesso = await acessoDoChat();
  if ("erro" in acesso) return acesso.erro;

  const aberta = await abrirConversa(threadId, acesso);
  if ("erro" in aberta) return aberta.erro;
  if (!aberta.podeResponder) return NextResponse.json({ error: "Sem permissao." }, { status: 403 });

  const form = await req.formData().catch(() => null);
  if (!form) return NextResponse.json({ error: "Envio invalido." }, { status: 400 });

  const corpo = texto(form.get("body")).slice(0, 2000);
  const marcarComoArte = texto(form.get("is_art")) === "true";
  const arquivo = form.get("file");

  /*
    Cartao da mensagem.

    Produto e cupom viajam como cartao, e nao como texto: do outro lado a pessoa
    ve a foto, o preco e o botao, em vez de um link cru que ninguem clica. Vem
    da tela ja pronto, mas o servidor so aceita o que sabe desenhar.
  */
  /* Responder citando e o recado que só a equipe lê. */
  const respondendo = texto(form.get("reply_to")) || null;
  const ehNotaInterna = texto(form.get("internal")) === "true" && aberta.conversa.kind !== "team";

  const cartaoCru = texto(form.get("card"));
  let cartao: Record<string, unknown> | null = null;
  if (cartaoCru) {
    try {
      const lido = JSON.parse(cartaoCru) as Record<string, unknown>;
      const tipo = texto(lido?.tipo);
      if (tipo === "produto" || tipo === "cupom") cartao = lido;
    } catch {
      /* Cartao ilegivel nao derruba a mensagem: vai o texto, que e o que
         importa. */
    }
  }

  let anexoUrl: string | null = null;
  let anexoNome: string | null = null;
  let anexoTipo: string | null = null;

  if (arquivo && typeof arquivo === "object" && "arrayBuffer" in arquivo) {
    const file = arquivo as File;
    if (file.size > TAMANHO_MAXIMO_DO_ANEXO) {
      return NextResponse.json({ error: "Arquivo maior que 8 MB." }, { status: 400 });
    }
    const extensao = (file.name.split(".").pop() || "bin").toLowerCase().replace(/[^a-z0-9]/g, "");
    const caminho = `${aberta.conversa.user_id}/conversas/${threadId}/${crypto.randomUUID()}.${extensao}`;
    const { error: erroUpload } = await aberta.admin.storage
      .from(BUCKET_PRIVADO_DA_CONVERSA)
      .upload(caminho, file, { contentType: file.type || "application/octet-stream", upsert: false });
    if (erroUpload) return NextResponse.json({ error: erroUpload.message }, { status: 400 });

    /* Pasta privada: grava o caminho; o endereco sai assinado na hora de ver. */
    anexoUrl = caminho;
    anexoNome = file.name || "arquivo";
    anexoTipo = String(file.type || "").startsWith("image/") ? "image" : "file";
  }

  if (!corpo && !anexoUrl && !cartao) {
    return NextResponse.json({ error: "Escreva uma mensagem ou anexe um arquivo." }, { status: 400 });
  }

  /* Arte so faz sentido na conversa com o cliente, e so em imagem: pedir
     aprovacao de um arquivo que ele nao consegue ver seria pedir um sim no
     escuro. */
  const ehArte = marcarComoArte && anexoTipo === "image" && aberta.conversa.kind !== "team";
  const agora = new Date().toISOString();

  /*
    Conversa de orcamento tem duas casas.

    A pagina publica do orcamento mostra a conversa presa a arte — e e la que o
    cliente esta olhando. Escrever aqui sem escrever la seria falar sozinho,
    entao a mensagem tem que achar uma arte para pousar. Sem nenhuma arte
    enviada ainda, o caminho e mandar pelo proprio orcamento, que e onde da
    para anexar a primeira.
  */
  let arteDoOrcamento: { art_id: string; quote_item_id: string } | null = null;
  if (aberta.conversa.kind === "quote" && aberta.conversa.quote_id) {
    const { data: ultima } = await aberta.admin
      .from("quote_item_art_messages")
      .select("art_id,quote_item_id")
      .eq("quote_id", aberta.conversa.quote_id)
      .order("created_at", { ascending: false })
      .limit(1)
      .maybeSingle();

    if (ultima?.art_id) {
      arteDoOrcamento = { art_id: String(ultima.art_id), quote_item_id: String(ultima.quote_item_id) };
    } else {
      const { data: primeira } = await aberta.admin
        .from("quote_item_arts")
        .select("id,quote_item_id")
        .eq("quote_id", aberta.conversa.quote_id)
        .order("created_at", { ascending: true })
        .limit(1)
        .maybeSingle();
      if (primeira?.id) {
        arteDoOrcamento = { art_id: String(primeira.id), quote_item_id: String(primeira.quote_item_id) };
      }
    }

    if (!arteDoOrcamento) {
      return NextResponse.json(
        {
          error:
            "Este orçamento ainda não tem arte enviada. Mande a primeira mensagem pela página do orçamento, junto com a arte.",
        },
        { status: 400 }
      );
    }

    if (anexoUrl) {
      return NextResponse.json(
        { error: "Para enviar arquivo em orçamento, use a página do orçamento — é lá que a arte fica registrada." },
        { status: 400 }
      );
    }
  }

  const { data: mensagem, error } = await aberta.admin
    .from("chat_messages")
    .insert({
      thread_id: threadId,
      user_id: aberta.conversa.user_id,
      author_type: aberta.autor,
      author_id: acesso.user.id,
      author_name: acesso.nome,
      body: corpo || null,
      attachment_url: null,
      attachment_path: anexoUrl,
      attachment_name: anexoNome,
      attachment_kind: anexoTipo,
      is_art: ehArte,
      art_status: ehArte ? "pending" : null,
      card: cartao,
      reply_to: respondendo,
      internal: ehNotaInterna,
      [aberta.lado === "store" ? "read_by_store_at" : "read_by_other_at"]: agora,
    })
    .select("id,created_at")
    .single();

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });

  /*
    A nota interna não é a conversa.

    Ela não vai para o cliente, não conta como não lida do outro lado e não
    aparece como última mensagem na lista — senão a loja veria "cuidado, esse
    aqui já deu calote" como resumo da conversa, com risco de alguém ler a
    frase em voz alta na frente do cliente.
  */
  /*
    Respondeu, zera o relógio.

    O cutucão de "está esperando" e o lembrete de retomar existem para trazer a
    pessoa de volta à conversa. Ela voltou: continuar cobrando seria cobrar algo
    que já foi feito — e é assim que se ensina alguém a ignorar aviso.
  */
  if (!ehNotaInterna) {
    await aberta.admin
      .from("chat_threads")
      .update({ nudged_at: null, follow_up_at: null })
      .eq("id", threadId);

    await atualizarResumoDaConversa(
      aberta.admin,
      threadId,
      { body: corpo, attachment_kind: anexoTipo, card: cartao, author_type: aberta.autor, created_at: mensagem.created_at },
      aberta.lado === "store" ? "other" : "store"
    );
  }

  if (ehNotaInterna) {
    /* O aviso ao vivo vai só para quem está com esta conversa aberta — a
       equipe. Nem cliente, nem push, nem sino. */
    await avisarConversa([canalDaConversa(threadId)], {
      threadId,
      autor: aberta.autor,
      autorNome: acesso.nome,
      resumo: "",
    });
    return NextResponse.json({ ok: true, id: mensagem.id, interna: true });
  }

  /* O outro lado da conversa do orcamento: a mensagem aparece na pagina que o
     cliente ja conhece. O vinculo evita que o gatilho a espelhe de volta. */
  if (arteDoOrcamento && aberta.conversa.quote_id) {
    await aberta.admin.from("quote_item_art_messages").insert({
      quote_id: aberta.conversa.quote_id,
      quote_item_id: arteDoOrcamento.quote_item_id,
      art_id: arteDoOrcamento.art_id,
      sender: "company",
      message: corpo,
      chat_message_id: mensagem.id,
    });
  }

  if (ehArte && aberta.conversa.order_id) {
    await aberta.admin
      .from("orders")
      .update({ art_status: "pending", art_message_id: mensagem.id, art_approved_at: null })
      .eq("id", aberta.conversa.order_id);
  }

  /* Na conversa da equipe quem precisa saber e a outra pessoa da dupla; na
     conversa da loja, o cliente. */
  const outroMembro =
    aberta.conversa.kind === "team"
      ? aberta.conversa.member_a === acesso.user.id
        ? aberta.conversa.member_b
        : aberta.conversa.member_a
      : null;

  await avisarConversa(
    [
      canalDaConversa(threadId),
      aberta.conversa.kind === "team" ? canalDaPessoa(outroMembro) : canalDoCliente(aberta.conversa.client_id),
    ],
    {
      threadId,
      autor: aberta.autor,
      autorNome: acesso.nome,
      resumo: corpo || (anexoTipo === "image" ? "Imagem" : anexoTipo ? "Arquivo" : ""),
      orderId: aberta.conversa.order_id,
      clientId: aberta.conversa.client_id,
    }
  );

  /* Visitante da loja: o aviso leva de volta para a loja com o chat aberto. */
  if (aberta.conversa.kind === "visitor") {
    const rotaDaLoja = await enderecoDaLoja(aberta.admin, aberta.conversa.user_id);
    if (rotaDaLoja) {
      await avisarVisitantePorPush(aberta.admin, {
        threadId,
        tenantId: aberta.conversa.user_id,
        titulo: "A loja respondeu",
        corpo: corpo || "Você recebeu uma mensagem.",
        rota: rotaDaLoja,
        tag: `loja-${threadId}`,
      });
    }
  }

  if (aberta.conversa.kind !== "team" && aberta.conversa.order_id && aberta.conversa.client_id) {
    const rotaDoCliente = await enderecoDoPortal(
      aberta.admin,
      aberta.conversa.user_id,
      aberta.conversa.order_id
    );
    if (rotaDoCliente) {
      await avisarClientePorPush(aberta.admin, {
        clientId: aberta.conversa.client_id,
        tenantId: aberta.conversa.user_id,
        titulo: aberta.conversa.title || "Mensagem da loja",
        corpo: corpo || (anexoTipo === "image" ? "A loja enviou uma imagem." : "A loja enviou um arquivo."),
        rota: rotaDoCliente,
        tag: `pedido-${aberta.conversa.order_id}`,
      });
    }
  }

  return NextResponse.json({ ok: true, id: mensagem.id });
}

/*
  Corrigir o que se escreveu.

  Errar o preço numa mensagem e só poder apagar é ruim dos dois lados: some a
  frase, o cliente vê "mensagem apagada" e fica com a pulga atrás da orelha. A
  correção fica marcada como editada — honesto e sem sumiço.
*/
export async function PATCH(req: Request, context: { params: Promise<{ threadId: string }> }) {
  const { threadId } = await context.params;
  const acesso = await acessoDoChat();
  if ("erro" in acesso) return acesso.erro;

  const aberta = await abrirConversa(threadId, acesso);
  if ("erro" in aberta) return aberta.erro;
  if (!aberta.podeResponder) return NextResponse.json({ error: "Sem permissao." }, { status: 403 });

  const corpo = (await req.json().catch(() => null)) as { messageId?: unknown; body?: unknown } | null;
  const messageId = texto(corpo?.messageId);
  const novoTexto = texto(corpo?.body).slice(0, 2000);
  if (!messageId) return NextResponse.json({ error: "Mensagem invalida." }, { status: 400 });
  if (!novoTexto) return NextResponse.json({ error: "A mensagem não pode ficar vazia." }, { status: 400 });

  const { data: mensagem } = await aberta.admin
    .from("chat_messages")
    .select("id,author_type,author_id,deleted_at,internal")
    .eq("id", messageId)
    .eq("thread_id", threadId)
    .maybeSingle();

  if (!mensagem?.id) return NextResponse.json({ error: "Mensagem nao encontrada." }, { status: 404 });
  if (mensagem.deleted_at) return NextResponse.json({ error: "Mensagem apagada." }, { status: 400 });

  const minha =
    aberta.conversa.kind === "team"
      ? mensagem.author_id === acesso.user.id
      : mensagem.author_type === "store" || mensagem.author_type === "team";

  if (!minha) return NextResponse.json({ error: "Só dá para editar a própria mensagem." }, { status: 400 });

  const { error } = await aberta.admin
    .from("chat_messages")
    .update({ body: novoTexto, edited_at: new Date().toISOString() })
    .eq("id", messageId);

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });

  if (!mensagem.internal) {
    await atualizarResumoDaConversa(
      aberta.admin,
      threadId,
      { body: novoTexto, author_type: aberta.autor },
      null
    );
  }

  await avisarConversa(
    [canalDaConversa(threadId), canalDoCliente(aberta.conversa.client_id)],
    { threadId, autor: aberta.autor, autorNome: acesso.nome, resumo: novoTexto.slice(0, 120) }
  );

  return NextResponse.json({ ok: true });
}

/* Apagar so vale para o que a propria pessoa escreveu. */
export async function DELETE(req: Request, context: { params: Promise<{ threadId: string }> }) {
  const { threadId } = await context.params;
  const acesso = await acessoDoChat();
  if ("erro" in acesso) return acesso.erro;

  const aberta = await abrirConversa(threadId, acesso);
  if ("erro" in aberta) return aberta.erro;
  if (!aberta.podeResponder) return NextResponse.json({ error: "Sem permissao." }, { status: 403 });

  const { searchParams } = new URL(req.url);
  const messageId = texto(searchParams.get("messageId"));
  if (!messageId) return NextResponse.json({ error: "Mensagem invalida." }, { status: 400 });

  const { data: mensagem } = await aberta.admin
    .from("chat_messages")
    .select("id,author_type,author_id,attachment_url,attachment_path")
    .eq("id", messageId)
    .eq("thread_id", threadId)
    .maybeSingle();

  if (!mensagem?.id) return NextResponse.json({ error: "Mensagem nao encontrada." }, { status: 404 });

  const minha =
    aberta.conversa.kind === "team"
      ? mensagem.author_id === acesso.user.id
      : mensagem.author_type === "store" || mensagem.author_type === "team";

  if (!minha) {
    return NextResponse.json({ error: "So da para apagar a propria mensagem." }, { status: 400 });
  }

  await apagarArquivoDaMensagem(aberta.admin, mensagem);

  const { error } = await aberta.admin
    .from("chat_messages")
    .update({
      deleted_at: new Date().toISOString(),
      deleted_by: acesso.nome,
      body: null,
      attachment_url: null,
      attachment_path: null,
      is_art: false,
      art_status: null,
    })
    .eq("id", messageId);

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });

  if (aberta.conversa.order_id) {
    await aberta.admin
      .from("orders")
      .update({ art_status: null, art_message_id: null, art_approved_at: null })
      .eq("id", aberta.conversa.order_id)
      .eq("art_message_id", messageId);
  }

  return NextResponse.json({ ok: true });
}
