import type { SupabaseClient } from "@supabase/supabase-js";

import { ErroDigital } from "./contexto";

/*
 * Os comentários das aulas, do lado da loja: as conversas (o comentário do
 * aluno e as respostas, um nível só), o que ainda não tem resposta da loja,
 * o que espera aprovação e o que foi ocultado.
 *
 * A loja vê o nome inteiro e o e-mail do aluno (são clientes dela); os outros
 * alunos só veem o primeiro nome e a inicial. A resposta da loja aparece na
 * área de membros com o nome e o selo da loja.
 */

export type FiltroDeComentarios = "sem_resposta" | "pendentes" | "todos" | "ocultos";
export type SituacaoDoComentario = "publicado" | "pendente" | "oculto";

export const FILTROS_DE_COMENTARIOS: readonly FiltroDeComentarios[] = ["sem_resposta", "pendentes", "todos", "ocultos"];

type LinhaDoComentario = {
  id: string;
  product_id: string;
  lesson_id: string;
  client_id: string | null;
  autor_equipe: string | null;
  parent_id: string | null;
  texto: string;
  status: SituacaoDoComentario;
  curtidas: number;
  criado_em: string;
  clients: { name: string | null; email: string | null } | null;
  digital_course_lessons: { titulo: string | null } | null;
  products: { name: string | null } | null;
};

export type RespostaNoPainel = {
  id: string;
  texto: string;
  status: SituacaoDoComentario;
  criadoEm: string;
  daLoja: boolean;
  minha: boolean;
  autor: string;
};

export type ComentarioNoPainel = {
  id: string;
  texto: string;
  status: SituacaoDoComentario;
  criadoEm: string;
  curtidas: number;
  daLoja: boolean;
  minha: boolean;
  aluno: { id: string; nome: string; email: string | null } | null;
  aula: { id: string; titulo: string };
  curso: { id: string; nome: string };
  respondido: boolean;
  respostas: RespostaNoPainel[];
};

/* "clients!..._client_id_fkey": as curtidas também ligam comentário e cliente,
   e sem dizer qual ligação o banco recusa a leitura. */
const COLUNAS =
  "id,product_id,lesson_id,client_id,autor_equipe,parent_id,texto,status,curtidas,criado_em,clients!digital_course_comments_client_id_fkey(name,email),digital_course_lessons(titulo),products(name)";
/* Conversas por página e o teto do que se lê para achar as sem resposta. */
const POR_PAGINA = 60;
const TETO_DA_BUSCA = 600;

export type AvisoDeComentario = { id: string; aluno: string; curso: string; aula: string; texto: string; pendente: boolean; criadoEm: string };

/**
 * O sino do painel: os comentários de aluno dos últimos 3 dias, quando a loja
 * pediu "Me avisar". O painel guarda o que cada pessoa já leu.
 */
export async function avisosDeComentarios(admin: SupabaseClient, lojaId: string): Promise<AvisoDeComentario[]> {
  const { data: ajustes } = await admin.from("digital_settings").select("config").eq("user_id", lojaId).maybeSingle();
  const comentarios = ((ajustes as { config?: { comentarios?: { avisar?: unknown } } } | null)?.config?.comentarios ?? {}) as { avisar?: unknown };
  if (comentarios.avisar === false) return [];
  const desde = new Date(Date.now() - 3 * 86400000).toISOString();
  const { data, error } = await admin
    .from("digital_course_comments")
    .select(COLUNAS)
    .eq("user_id", lojaId)
    .not("client_id", "is", null)
    .is("autor_equipe", null)
    .is("apagado_em", null)
    .in("status", ["publicado", "pendente"])
    .gte("criado_em", desde)
    .order("criado_em", { ascending: false })
    .limit(15);
  if (error) throw new ErroDigital("Não foi possível ler os comentários.", 500);
  return ((data ?? []) as unknown as LinhaDoComentario[]).map((c) => ({
    id: c.id,
    aluno: c.clients?.name?.trim() || "Cliente",
    curso: c.products?.name?.trim() || "Conteúdo",
    aula: c.digital_course_lessons?.titulo?.trim() || "Vídeo",
    texto: c.texto.slice(0, 200),
    pendente: c.status === "pendente",
    criadoEm: c.criado_em,
  }));
}

/** Quantas conversas esperam a loja: sem resposta dela, e esperando aprovação. */
export async function contarComentarios(admin: SupabaseClient, lojaId: string): Promise<{ semResposta: number; pendentes: number }> {
  const [principais, respondidos, pendentes] = await Promise.all([
    admin
      .from("digital_course_comments")
      .select("id")
      .eq("user_id", lojaId)
      .is("parent_id", null)
      .not("client_id", "is", null)
      .is("apagado_em", null)
      .in("status", ["publicado", "pendente"])
      .order("criado_em", { ascending: false })
      .limit(2000),
    admin
      .from("digital_course_comments")
      .select("parent_id")
      .eq("user_id", lojaId)
      .not("autor_equipe", "is", null)
      .not("parent_id", "is", null)
      .is("apagado_em", null)
      .limit(5000),
    admin.from("digital_course_comments").select("id", { count: "exact", head: true }).eq("user_id", lojaId).eq("status", "pendente").is("apagado_em", null),
  ]);
  const feitos = new Set(((respondidos.data ?? []) as Array<{ parent_id: string }>).map((r) => r.parent_id));
  const semResposta = ((principais.data ?? []) as Array<{ id: string }>).filter((c) => !feitos.has(c.id)).length;
  return { semResposta, pendentes: pendentes.count ?? 0 };
}

/** As conversas de um filtro (as mais novas primeiro), com as respostas. */
export async function listarComentarios(
  admin: SupabaseClient,
  params: { lojaId: string; eu: string; nomeDaLoja: string; filtro: FiltroDeComentarios; cursoId?: string | null; antes?: string | null }
): Promise<{ comentarios: ComentarioNoPainel[]; maisAntigos: string | null }> {
  const { lojaId, eu, nomeDaLoja, filtro } = params;
  const cursoId = params.cursoId && /^[0-9a-f-]{36}$/i.test(params.cursoId) ? params.cursoId : null;
  const antes = params.antes && !Number.isNaN(new Date(params.antes).getTime()) ? new Date(params.antes).toISOString() : null;

  /* 1. As conversas (o comentário principal de cada uma). */
  let raizes: LinhaDoComentario[] = [];
  if (filtro === "pendentes" || filtro === "ocultos") {
    /* Pendente ou oculto pode ser uma resposta: a conversa inteira vem junto. */
    let consulta = admin
      .from("digital_course_comments")
      .select("id,parent_id,criado_em")
      .eq("user_id", lojaId)
      .eq("status", filtro === "pendentes" ? "pendente" : "oculto")
      .is("apagado_em", null)
      .order("criado_em", { ascending: false })
      .limit(TETO_DA_BUSCA);
    if (cursoId) consulta = consulta.eq("product_id", cursoId);
    const { data } = await consulta;
    const ids = Array.from(new Set(((data ?? []) as Array<{ id: string; parent_id: string | null }>).map((c) => c.parent_id ?? c.id))).slice(0, POR_PAGINA * 2);
    if (ids.length) {
      let das = admin.from("digital_course_comments").select(COLUNAS).eq("user_id", lojaId).in("id", ids).is("apagado_em", null).order("criado_em", { ascending: false });
      if (antes) das = das.lt("criado_em", antes);
      const lidas = await das;
      if (lidas.error) throw new ErroDigital("Não foi possível ler os comentários agora.", 500);
      raizes = (lidas.data ?? []) as unknown as LinhaDoComentario[];
    }
  } else {
    let consulta = admin
      .from("digital_course_comments")
      .select(COLUNAS)
      .eq("user_id", lojaId)
      .is("parent_id", null)
      .is("apagado_em", null)
      .in("status", ["publicado", "pendente"])
      .order("criado_em", { ascending: false })
      .limit(filtro === "sem_resposta" ? TETO_DA_BUSCA : POR_PAGINA + 1);
    if (filtro === "sem_resposta") consulta = consulta.not("client_id", "is", null);
    if (cursoId) consulta = consulta.eq("product_id", cursoId);
    if (antes) consulta = consulta.lt("criado_em", antes);
    const lidas = await consulta;
    if (lidas.error) throw new ErroDigital("Não foi possível ler os comentários agora.", 500);
    raizes = (lidas.data ?? []) as unknown as LinhaDoComentario[];
  }

  /* 2. As respostas das conversas. */
  const respostasPorConversa = new Map<string, LinhaDoComentario[]>();
  const lerRespostas = async (ids: string[]) => {
    for (let i = 0; i < ids.length; i += 100) {
      const { data, error } = await admin
        .from("digital_course_comments")
        .select(COLUNAS)
        .eq("user_id", lojaId)
        .in("parent_id", ids.slice(i, i + 100))
        .is("apagado_em", null)
        .order("criado_em", { ascending: true })
        .limit(2000);
      if (error) throw new ErroDigital("Não foi possível ler as respostas agora.", 500);
      for (const resposta of (data ?? []) as unknown as LinhaDoComentario[]) {
        const lista = respostasPorConversa.get(String(resposta.parent_id)) ?? [];
        lista.push(resposta);
        respostasPorConversa.set(String(resposta.parent_id), lista);
      }
    }
  };
  await lerRespostas(raizes.map((r) => r.id));
  const respondida = (id: string) => (respostasPorConversa.get(id) ?? []).some((r) => r.autor_equipe);

  if (filtro === "sem_resposta") raizes = raizes.filter((r) => !respondida(r.id));
  const pagina = raizes.slice(0, POR_PAGINA);
  const maisAntigos = raizes.length > POR_PAGINA ? pagina[pagina.length - 1]?.criado_em ?? null : null;

  const nomeDoAluno = (linha: LinhaDoComentario) => linha.clients?.name?.trim() || linha.clients?.email || "Cliente";
  return {
    maisAntigos,
    comentarios: pagina.map((linha) => ({
      id: linha.id,
      texto: linha.texto,
      status: linha.status,
      criadoEm: linha.criado_em,
      curtidas: linha.curtidas,
      daLoja: Boolean(linha.autor_equipe),
      minha: linha.autor_equipe === eu,
      aluno: linha.client_id ? { id: linha.client_id, nome: nomeDoAluno(linha), email: linha.clients?.email ?? null } : null,
      aula: { id: linha.lesson_id, titulo: linha.digital_course_lessons?.titulo ?? "Vídeo" },
      curso: { id: linha.product_id, nome: linha.products?.name ?? "Conteúdo" },
      respondido: respondida(linha.id),
      respostas: (respostasPorConversa.get(linha.id) ?? []).map((r) => ({
        id: r.id,
        texto: r.texto,
        status: r.status,
        criadoEm: r.criado_em,
        daLoja: Boolean(r.autor_equipe),
        minha: r.autor_equipe === eu,
        autor: r.autor_equipe ? nomeDaLoja : nomeDoAluno(r),
      })),
    })),
  };
}

async function lerComentarioDaLoja(admin: SupabaseClient, lojaId: string, id: string) {
  if (!/^[0-9a-f-]{36}$/i.test(id)) throw new ErroDigital("Comentário não encontrado.", 404);
  const { data } = await admin
    .from("digital_course_comments")
    .select("id,product_id,lesson_id,parent_id,status,autor_equipe")
    .eq("id", id)
    .eq("user_id", lojaId)
    .is("apagado_em", null)
    .maybeSingle();
  const linha = data as { id: string; product_id: string; lesson_id: string; parent_id: string | null; status: SituacaoDoComentario; autor_equipe: string | null } | null;
  if (!linha) throw new ErroDigital("Comentário não encontrado. Ele pode ter sido apagado.", 404);
  return linha;
}

/**
 * A loja responde (com o nome e o selo dela). Responder a uma pergunta que
 * esperava aprovação também a publica: a loja já está conversando com ela.
 */
export async function responderComentario(admin: SupabaseClient, params: { lojaId: string; eu: string; id: string; texto: unknown }): Promise<{ id: string }> {
  const texto = typeof params.texto === "string" ? params.texto.replace(/\u0000/g, "").trim().slice(0, 2000) : "";
  if (!texto) throw new ErroDigital("Escreva a resposta.", 400);
  const comentario = await lerComentarioDaLoja(admin, params.lojaId, params.id);
  const raiz = comentario.parent_id ?? comentario.id;
  const { data, error } = await admin
    .from("digital_course_comments")
    .insert({
      user_id: params.lojaId,
      product_id: comentario.product_id,
      lesson_id: comentario.lesson_id,
      client_id: null,
      autor_equipe: params.eu,
      parent_id: raiz,
      texto,
      status: "publicado",
    })
    .select("id")
    .single();
  if (error || !data) throw new ErroDigital("Não foi possível publicar a resposta agora.", 500);
  await admin.from("digital_course_comments").update({ status: "publicado" }).eq("user_id", params.lojaId).eq("status", "pendente").in("id", Array.from(new Set([raiz, comentario.id])));
  return { id: (data as { id: string }).id };
}

/** Aprovar (publicar), ocultar ou mostrar de novo. */
export async function mudarSituacaoDoComentario(admin: SupabaseClient, params: { lojaId: string; id: string; status: unknown }): Promise<void> {
  const status = params.status === "publicado" || params.status === "oculto" ? params.status : null;
  if (!status) throw new ErroDigital("Escolha publicar ou ocultar.", 400);
  await lerComentarioDaLoja(admin, params.lojaId, params.id);
  const { error } = await admin.from("digital_course_comments").update({ status }).eq("id", params.id).eq("user_id", params.lojaId);
  if (error) throw new ErroDigital("Não foi possível mudar o comentário agora.", 500);
}

/** Apagar (some para todos; as respostas da conversa somem junto). */
export async function apagarComentario(admin: SupabaseClient, params: { lojaId: string; id: string }): Promise<void> {
  const comentario = await lerComentarioDaLoja(admin, params.lojaId, params.id);
  const agora = new Date().toISOString();
  const { error } = await admin.from("digital_course_comments").update({ apagado_em: agora }).eq("id", comentario.id).eq("user_id", params.lojaId);
  if (error) throw new ErroDigital("Não foi possível apagar agora.", 500);
  if (!comentario.parent_id) {
    await admin.from("digital_course_comments").update({ apagado_em: agora }).eq("parent_id", comentario.id).eq("user_id", params.lojaId).is("apagado_em", null);
  }
}
