import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { avisarConversa, canalDaConversa, canalDaLoja } from "@/lib/chats-aviso";
import { avisarLojaPorPush } from "@/lib/chats-push";
import { createAdminClient } from "@/lib/supabase-admin";
import {
  BUCKET_PRIVADO_DA_CONVERSA,
  TAMANHO_MAXIMO_DO_ANEXO,
  atualizarResumoDaConversa,
  conversaDoPedido,
  listarMensagensDaConversa,
  marcarConversaComoLidaPeloCliente,
  mensagemParaTela,
} from "@/lib/chats-servidor";

/*
  Conversa do pedido, lado do cliente.

  Mesmo fio que a loja ve no admin — as duas portas abrem a mesma conversa do
  Chats Ktalogu. O cliente responde, manda arquivo e decide a arte: a aprovacao
  fica presa a mensagem da arte e tambem no pedido, para a producao ver sem
  abrir a conversa.
*/

type SessionPayload = { client?: { id?: string; name?: string } | null };

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

async function resolverSessao(slug: string) {
  const cookieStore = await cookies();
  const token = texto(cookieStore.get("ks_client_portal")?.value);
  if (!slug || !token) return null;

  const admin = createAdminClient();
  const sessao = await admin.rpc("client_portal_get_session", { p_slug: slug, p_session_token: token });
  const cliente = (sessao.data as SessionPayload | null)?.client || null;
  if (sessao.error || !cliente?.id) return null;
  return { admin, clientId: String(cliente.id), clientName: texto(cliente.name) || "Cliente" };
}

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

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const slug = texto(searchParams.get("slug"));
  const orderId = texto(searchParams.get("orderId"));

  const sessao = await resolverSessao(slug);
  if (!sessao) return NextResponse.json({ error: "Sessao invalida." }, { status: 401 });

  const pedido = await pedidoDoCliente(sessao.admin, orderId, sessao.clientId);
  if (!pedido) return NextResponse.json({ error: "Pedido nao encontrado." }, { status: 404 });

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

  /* So grava leitura quando ha o que marcar. */
  if (mensagens.some((mensagem) => mensagem.author_type !== "client" && !mensagem.read_by_other_at)) {
    await marcarConversaComoLidaPeloCliente(sessao.admin, conversa.id);
  }

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

export async function POST(request: Request) {
  const form = await request.formData().catch(() => null);
  if (!form) return NextResponse.json({ error: "Envio invalido." }, { status: 400 });

  const slug = texto(form.get("slug"));
  const orderId = texto(form.get("orderId"));

  const sessao = await resolverSessao(slug);
  if (!sessao) return NextResponse.json({ error: "Sessao invalida." }, { status: 401 });

  const pedido = await pedidoDoCliente(sessao.admin, orderId, sessao.clientId);
  if (!pedido) return NextResponse.json({ error: "Pedido nao encontrado." }, { status: 404 });

  const corpo = texto(form.get("body")).slice(0, 2000);
  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 extensao = (file.name.split(".").pop() || "bin").toLowerCase().replace(/[^a-z0-9]/g, "");
    const caminho = `${pedido.user_id}/pedidos/${pedido.id}/cliente-${crypto.randomUUID()}.${extensao}`;
    const { error: erroUpload } = await sessao.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) {
    return NextResponse.json({ error: "Escreva uma mensagem ou anexe um arquivo." }, { status: 400 });
  }

  const conversa = await conversaDoPedido(sessao.admin, pedido);
  const { data: mensagem, error } = await sessao.admin
    .from("chat_messages")
    .insert({
      thread_id: conversa.id,
      user_id: pedido.user_id,
      author_type: "client",
      author_id: sessao.clientId,
      author_name: sessao.clientName,
      body: corpo || null,
      attachment_url: null,
      attachment_path: anexoUrl,
      attachment_name: anexoNome,
      attachment_kind: anexoTipo,
      read_by_other_at: new Date().toISOString(),
    })
    .select("id,created_at")
    .single();

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

  await atualizarResumoDaConversa(sessao.admin, conversa.id, {
    body: corpo,
    attachment_kind: anexoTipo,
    created_at: mensagem.created_at,
  });

  await avisarConversa([canalDaConversa(conversa.id), canalDaLoja(pedido.user_id)], {
    threadId: conversa.id,
    autor: "client",
    autorNome: sessao.clientName,
    resumo: corpo || (anexoTipo === "image" ? "Imagem" : anexoTipo ? "Arquivo" : ""),
    orderId: pedido.id,
    orderNumber: pedido.number,
    clientId: sessao.clientId,
  });

  await avisarLojaPorPush(sessao.admin, {
    tenantId: pedido.user_id,
    titulo: `${sessao.clientName} · pedido #${pedido.number}`,
    corpo: corpo || (anexoTipo === "image" ? "Enviou uma imagem." : "Enviou um arquivo."),
    rota: `/chats?conversa=${conversa.id}`,
    tag: `pedido-${pedido.id}`,
  });

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

/* Aprovacao ou reprovacao da arte. */
export async function PATCH(request: Request) {
  const body = (await request.json().catch(() => null)) as
    | { slug?: unknown; orderId?: unknown; messageId?: unknown; decision?: unknown; note?: unknown }
    | null;

  const slug = texto(body?.slug);
  const orderId = texto(body?.orderId);
  const messageId = texto(body?.messageId);
  const decisao = texto(body?.decision);
  const observacao = texto(body?.note).slice(0, 500);

  if (decisao !== "approved" && decisao !== "rejected") {
    return NextResponse.json({ error: "Decisao invalida." }, { status: 400 });
  }

  const sessao = await resolverSessao(slug);
  if (!sessao) return NextResponse.json({ error: "Sessao invalida." }, { status: 401 });

  const pedido = await pedidoDoCliente(sessao.admin, orderId, sessao.clientId);
  if (!pedido) return NextResponse.json({ error: "Pedido nao encontrado." }, { status: 404 });

  const conversa = await conversaDoPedido(sessao.admin, pedido);
  const agora = new Date().toISOString();
  const { data: arte, error } = await sessao.admin
    .from("chat_messages")
    .update({ art_status: decisao, art_decided_at: agora, art_decision_note: observacao || null })
    .eq("id", messageId)
    .eq("thread_id", conversa.id)
    .eq("is_art", true)
    .select("id")
    .maybeSingle();

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });
  if (!arte?.id) return NextResponse.json({ error: "Arte nao encontrada." }, { status: 404 });

  /* O pedido guarda o resultado para a producao nao precisar abrir a conversa. */
  await sessao.admin
    .from("orders")
    .update({
      art_status: decisao,
      art_message_id: arte.id,
      art_approved_at: decisao === "approved" ? agora : null,
    })
    .eq("id", orderId);

  /* A decisao vira mensagem no fio, para a loja ler junto com o resto. */
  const recado =
    decisao === "approved"
      ? `Arte aprovada.${observacao ? ` ${observacao}` : ""}`
      : `Arte reprovada.${observacao ? ` ${observacao}` : ""}`;

  const { data: aviso } = await sessao.admin
    .from("chat_messages")
    .insert({
      thread_id: conversa.id,
      user_id: pedido.user_id,
      author_type: "client",
      author_id: sessao.clientId,
      author_name: sessao.clientName,
      body: recado,
      read_by_other_at: agora,
    })
    .select("created_at")
    .single();

  await atualizarResumoDaConversa(sessao.admin, conversa.id, {
    body: recado,
    created_at: aviso?.created_at || agora,
  });

  await avisarConversa([canalDaConversa(conversa.id), canalDaLoja(pedido.user_id)], {
    threadId: conversa.id,
    autor: "client",
    autorNome: sessao.clientName,
    resumo: recado,
    orderId: pedido.id,
    orderNumber: pedido.number,
    clientId: sessao.clientId,
  });

  await avisarLojaPorPush(sessao.admin, {
    tenantId: pedido.user_id,
    titulo: `${sessao.clientName} · pedido #${pedido.number}`,
    corpo: recado,
    rota: `/chats?conversa=${conversa.id}`,
    tag: `pedido-${pedido.id}`,
  });

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