import { NextResponse } from "next/server";
import { avisarConversa, canalDaConversa, canalDoCliente } from "@/lib/chats/aviso";
import { avisarClientePorPush, enderecoDoPortal } from "@/lib/chats/push";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import {
  BUCKET_PRIVADO_DA_CONVERSA,
  TAMANHO_MAXIMO_DO_ANEXO,
  apagarArquivoDaMensagem,
  atualizarResumoDaConversa,
  conversaDoPedido,
  listarMensagensDaConversa,
  marcarConversaComoLida,
  mensagemParaTela,
} from "@/lib/chats/servidor";

/*
  Conversa do pedido, lado da loja.

  O recado antigo era um campo de texto no pedido: mao unica, sem historico e
  sem anexo. Aqui a loja le o fio inteiro e responde, podendo mandar arquivo e
  marcar uma imagem como ARTE — e a arte marcada e a que o cliente aprova ou
  reprova no portal.

  O fio em si vive nas conversas do Chats Ktalogu; esta rota e so a porta de
  entrada pelo pedido.
*/

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

async function autorizar(permissao: "orders.read" | "orders.edit") {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return { erro: NextResponse.json({ error: "Nao autenticado." }, { status: 401 }) } as const;

  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, permissao)) {
    return { erro: NextResponse.json({ error: "Sem permissao." }, { status: 403 }) } as const;
  }
  const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
  const nome = texto(user.user_metadata?.name || user.user_metadata?.full_name || user.email) || "Loja";
  return { user, ctx, tenantIds, nome } as const;
}

async function pedidoDoTenant(admin: ReturnType<typeof createAdminClient>, orderId: string, tenantIds: string[]) {
  const { data } = await admin
    .from("orders")
    .select("id,user_id,number,client_id")
    .eq("id", orderId)
    .in("user_id", tenantIds)
    .maybeSingle();
  return data as { id: string; user_id: string; number: number; client_id: string | null } | null;
}

export async function GET(_req: Request, context: { params: Promise<{ id: string }> }) {
  const { id } = await context.params;
  const acesso = await autorizar("orders.read");
  if ("erro" in acesso) return acesso.erro;

  const admin = createAdminClient();
  const pedido = await pedidoDoTenant(admin, id, acesso.tenantIds);
  if (!pedido) return NextResponse.json({ error: "Pedido nao encontrado." }, { status: 404 });

  const conversa = await conversaDoPedido(admin, pedido);
  const mensagens = await listarMensagensDaConversa(admin, conversa.id);

  /* Marcar leitura e uma escrita: so vale a pena quando existe algo por ler.
     Sem isso, cada volta da conversa gravava no banco a toa. */
  if (mensagens.some((mensagem) => mensagem.author_type === "client" && !mensagem.read_by_store_at)) {
    await marcarConversaComoLida(admin, conversa.id, "store");
  }

  return NextResponse.json({ ok: true, threadId: conversa.id, mensagens: mensagens.map((mensagem) => mensagemParaTela(mensagem)) });
}

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

  const admin = createAdminClient();
  const pedido = await pedidoDoTenant(admin, id, acesso.tenantIds);
  if (!pedido) return NextResponse.json({ error: "Pedido nao encontrado." }, { status: 404 });

  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");

  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 ehImagem = String(file.type || "").startsWith("image/");
    const extensao = (file.name.split(".").pop() || "bin").toLowerCase().replace(/[^a-z0-9]/g, "");
    const caminho = `${pedido.user_id}/pedidos/${pedido.id}/${crypto.randomUUID()}.${extensao}`;
    const { error: erroUpload } = await 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 = ehImagem ? "image" : "file";
  }

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

  /* So imagem vira arte: pedir aprovacao de um PDF que o cliente nao consegue
     abrir na tela seria pedir um "sim" no escuro. */
  const ehArte = marcarComoArte && anexoTipo === "image";
  const conversa = await conversaDoPedido(admin, pedido);

  const { data: mensagem, error } = await admin
    .from("chat_messages")
    .insert({
      thread_id: conversa.id,
      user_id: pedido.user_id,
      author_type: "store",
      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,
      read_by_store_at: new Date().toISOString(),
    })
    .select("id,created_at")
    .single();

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

  await atualizarResumoDaConversa(
    admin,
    conversa.id,
    { body: corpo, attachment_kind: anexoTipo, author_type: "store", created_at: mensagem.created_at },
    "other"
  );

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

  await avisarConversa([canalDaConversa(conversa.id), canalDoCliente(pedido.client_id)], {
    threadId: conversa.id,
    autor: "store",
    autorNome: acesso.nome,
    resumo: corpo || (anexoTipo === "image" ? "Imagem" : anexoTipo ? "Arquivo" : ""),
    orderId: pedido.id,
    orderNumber: pedido.number,
    clientId: pedido.client_id,
  });

  /* E no celular do cliente que o aviso alcanca quem esta fora do portal. */
  const rotaDoCliente = await enderecoDoPortal(admin, pedido.user_id, pedido.id);
  if (rotaDoCliente) {
    await avisarClientePorPush(admin, {
      clientId: pedido.client_id,
      tenantId: pedido.user_id,
      titulo: `Mensagem sobre o pedido #${pedido.number}`,
      corpo: corpo || (anexoTipo === "image" ? "A loja enviou uma imagem." : "A loja enviou um arquivo."),
      rota: rotaDoCliente,
      tag: `pedido-${pedido.id}`,
    });
  }

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

/*
  Apaga uma mensagem da loja.

  So a loja apaga o que a loja escreveu — apagar a fala do cliente seria reescrever
  a conversa dele. O arquivo sai do armazenamento junto, senao o link continuaria
  aberto para quem tivesse guardado o endereco.
*/
export async function DELETE(req: Request, context: { params: Promise<{ id: string }> }) {
  const { id } = await context.params;
  const acesso = await autorizar("orders.edit");
  if ("erro" in acesso) return acesso.erro;

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

  const admin = createAdminClient();
  const pedido = await pedidoDoTenant(admin, id, acesso.tenantIds);
  if (!pedido) return NextResponse.json({ error: "Pedido nao encontrado." }, { status: 404 });

  const conversa = await conversaDoPedido(admin, pedido);
  const { data: mensagem } = await admin
    .from("chat_messages")
    .select("id,author_type,attachment_url,attachment_path,is_art")
    .eq("id", messageId)
    .eq("thread_id", conversa.id)
    .maybeSingle();

  if (!mensagem?.id) return NextResponse.json({ error: "Mensagem nao encontrada." }, { status: 404 });
  if (mensagem.author_type === "client") {
    return NextResponse.json({ error: "So da para apagar mensagem enviada pela loja." }, { status: 400 });
  }

  await apagarArquivoDaMensagem(admin, mensagem);

  const { error } = await 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 });

  /* Se a arte apagada era a que estava valendo, o pedido volta a nao ter arte. */
  await admin
    .from("orders")
    .update({ art_status: null, art_message_id: null, art_approved_at: null })
    .eq("id", id)
    .eq("art_message_id", messageId);

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