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

import { tamanhoLegivel } from "../nome-do-arquivo";
import { lerConfigDigital, resumoDoQueRecebe, type ConfigDigital, type TipoDeEntregavel } from "../produto";
import { ErroDigital } from "./contexto";

/*
 * Leitura e escrita dos produtos digitais no banco, sempre presas à loja
 * (`lojaId`) e ao tipo digital: uma rota dos digitais nunca mexe num produto
 * físico, e vice-versa.
 */

export const COLUNAS_DO_PRODUTO =
  "id,name,description,price,image_url,category_id,show_in_catalog,active,digital_config,created_at";

export type ProdutoDigitalNoBanco = {
  id: string;
  name: string;
  description: string | null;
  price: number;
  image_url: string | null;
  category_id: string | null;
  show_in_catalog: boolean | null;
  active: boolean | null;
  digital_config: unknown;
  created_at: string;
};

export type ArquivoNaTela = {
  id: string;
  nome: string;
  extensao: string;
  categoria: string;
  tamanho: number;
  tamanhoLegivel: string;
  status: string;
};

export type EntregavelNaTela = {
  id: string;
  tipo: TipoDeEntregavel;
  titulo: string;
  descricao: string | null;
  url: string | null;
  instrucoes: string | null;
  valePara: string | null;
  posicao: number;
  ativo: boolean;
  arquivo: ArquivoNaTela | null;
};

type LinhaDoEntregavel = {
  id: string;
  tipo: TipoDeEntregavel;
  titulo: string;
  descricao: string | null;
  url: string | null;
  instrucoes: string | null;
  vale_para: string | null;
  posicao: number;
  ativo: boolean;
  digital_files: { id: string; nome: string; extensao: string; categoria: string; tamanho_bytes: number; status: string } | null;
};

export function arquivoNaTela(linha: { id: string; nome: string; extensao: string; categoria: string; tamanho_bytes: number; status: string }): ArquivoNaTela {
  return {
    id: linha.id,
    nome: linha.nome,
    extensao: linha.extensao,
    categoria: linha.categoria,
    tamanho: Number(linha.tamanho_bytes) || 0,
    tamanhoLegivel: tamanhoLegivel(Number(linha.tamanho_bytes) || 0),
    status: linha.status,
  };
}

export async function lerProdutoDaLoja(admin: SupabaseClient, lojaId: string, id: string): Promise<ProdutoDigitalNoBanco> {
  if (!/^[0-9a-f-]{36}$/i.test(id)) throw new ErroDigital("Produto não encontrado.", 404);
  const { data, error } = await admin
    .from("products")
    .select(COLUNAS_DO_PRODUTO)
    .eq("id", id)
    .eq("user_id", lojaId)
    .eq("product_kind", "digital")
    .maybeSingle();
  if (error) throw new ErroDigital("Não foi possível ler o produto.", 500);
  if (!data) throw new ErroDigital("Produto não encontrado.", 404);
  return data as ProdutoDigitalNoBanco;
}

export async function lerEntregaveis(admin: SupabaseClient, lojaId: string, productId: string): Promise<EntregavelNaTela[]> {
  const { data, error } = await admin
    .from("digital_deliverables")
    .select("id,tipo,titulo,descricao,url,instrucoes,vale_para,posicao,ativo,digital_files(id,nome,extensao,categoria,tamanho_bytes,status)")
    .eq("user_id", lojaId)
    .eq("product_id", productId)
    .is("apagado_em", null)
    .order("posicao", { ascending: true })
    .order("criado_em", { ascending: true });
  if (error) throw new ErroDigital("Não foi possível ler o que o comprador recebe.", 500);
  return ((data ?? []) as unknown as LinhaDoEntregavel[]).map((linha) => ({
    id: linha.id,
    tipo: linha.tipo,
    titulo: linha.titulo,
    descricao: linha.descricao,
    url: linha.url,
    instrucoes: linha.instrucoes,
    valePara: linha.vale_para,
    posicao: linha.posicao,
    ativo: linha.ativo,
    arquivo: linha.digital_files ? arquivoNaTela(linha.digital_files) : null,
  }));
}

/*
 * "O que você recebe" da loja é montado dos entregáveis e guardado na config
 * pública do produto, sem endereço nenhum. Roda depois de toda mudança nos
 * entregáveis.
 */
export async function atualizarOQueRecebe(admin: SupabaseClient, lojaId: string, productId: string): Promise<ConfigDigital> {
  const [produto, entregaveis] = await Promise.all([
    lerProdutoDaLoja(admin, lojaId, productId),
    lerEntregaveis(admin, lojaId, productId),
  ]);
  const config = lerConfigDigital(produto.digital_config);
  const atualizada: ConfigDigital = {
    ...config,
    recebe: resumoDoQueRecebe(
      entregaveis.map((e) => ({
        tipo: e.tipo,
        titulo: e.titulo,
        descricao: e.descricao,
        ativo: e.ativo,
        arquivo: e.arquivo ? { extensao: e.arquivo.extensao, tamanhoLegivel: e.arquivo.tamanhoLegivel } : null,
      }))
    ),
  };
  const { error } = await admin
    .from("products")
    .update({ digital_config: atualizada })
    .eq("id", productId)
    .eq("user_id", lojaId)
    .eq("product_kind", "digital");
  if (error) throw new ErroDigital("Não foi possível atualizar a página do produto.", 500);
  return atualizada;
}

/* Quantas vendas o produto já teve (decide se pode apagar ou trocar de tipo). */
export async function vendasDoProduto(admin: SupabaseClient, productId: string): Promise<number> {
  const { count, error } = await admin.from("order_items").select("id", { count: "exact", head: true }).eq("product_id", productId);
  if (error) throw new ErroDigital("Não foi possível conferir as vendas do produto.", 500);
  return count ?? 0;
}

/*
 * Sob encomenda: o produto tem um item "Arquivo feito para você" (a unidade
 * que espera o arquivo final que a loja anexa no pedido). Voltar para "na
 * hora" desliga o item; quem já comprou continua esperando o seu arquivo.
 */
export async function sincronizarEncomenda(admin: SupabaseClient, lojaId: string, productId: string, modo: "na_hora" | "encomenda"): Promise<void> {
  const { data } = await admin
    .from("digital_deliverables")
    .select("id,ativo")
    .eq("user_id", lojaId)
    .eq("product_id", productId)
    .eq("tipo", "encomenda")
    .is("apagado_em", null)
    .order("criado_em", { ascending: true });
  const existentes = (data ?? []) as Array<{ id: string; ativo: boolean }>;
  const agora = new Date().toISOString();
  if (modo === "encomenda") {
    if (!existentes.length) {
      const { error } = await admin.from("digital_deliverables").insert({
        user_id: lojaId,
        product_id: productId,
        tipo: "encomenda",
        titulo: "Arquivo feito para você",
        descricao: "A loja prepara depois da compra e envia por aqui.",
        posicao: 0,
      });
      if (error) throw new ErroDigital("Não foi possível preparar a entrega sob encomenda.", 500);
    } else if (!existentes.some((e) => e.ativo)) {
      await admin.from("digital_deliverables").update({ ativo: true, atualizado_em: agora }).eq("id", existentes[0].id);
    }
  } else if (existentes.some((e) => e.ativo)) {
    await admin
      .from("digital_deliverables")
      .update({ ativo: false, atualizado_em: agora })
      .eq("user_id", lojaId)
      .eq("product_id", productId)
      .eq("tipo", "encomenda");
  } else {
    return;
  }
  await atualizarOQueRecebe(admin, lojaId, productId);
}
