import { soDigitos } from "@/lib/fiscal/montar";

import type { Admin } from "./base";
import { ErroFiscal } from "./notas";

export type ProdutoFiscal = {
  id: string;
  nome: string;
  sku: string | null;
  imagem: string | null;
  ativo: boolean;
  unidade: string | null;
  fiscalTipo: "mercadoria" | "servico" | null;
  ncm: string | null;
  cest: string | null;
  origem: number | null;
  perfilId: string | null;
};

export type ServicoFiscal = {
  id: string;
  titulo: string;
  preco: number;
  ativo: boolean;
  fiscalTipo: "produto" | "servico" | null;
  perfilId: string | null;
};

const texto = (valor: unknown) => (typeof valor === "string" ? valor.trim() : "");

export async function listarProdutosFiscais(admin: Admin, empresaId: string): Promise<ProdutoFiscal[]> {
  const { data, error } = await admin
    .from("products")
    .select("id,name,sku,image_url,active,unit_type,fiscal_tipo,ncm,cest,fiscal_origem,fiscal_perfil_id")
    .eq("user_id", empresaId)
    .neq("product_kind", "digital")
    .order("name", { ascending: true })
    .limit(5000);
  if (error) throw new ErroFiscal(`Falha ao ler os produtos: ${error.message}`, 500);
  return ((data ?? []) as Array<Record<string, unknown>>).map((p) => ({
    id: String(p.id),
    nome: texto(p.name) || "Produto sem nome",
    sku: texto(p.sku) || null,
    imagem: texto(p.image_url) || null,
    ativo: p.active !== false,
    unidade: texto(p.unit_type) || null,
    fiscalTipo: p.fiscal_tipo === "servico" ? "servico" : p.fiscal_tipo === "mercadoria" ? "mercadoria" : null,
    ncm: texto(p.ncm) || null,
    cest: texto(p.cest) || null,
    origem: p.fiscal_origem === null || p.fiscal_origem === undefined ? null : Number(p.fiscal_origem),
    perfilId: texto(p.fiscal_perfil_id) || null,
  }));
}

export async function listarServicosFiscais(admin: Admin, empresaId: string): Promise<ServicoFiscal[]> {
  const { data, error } = await admin
    .from("optional_services")
    .select("id,title,price,active,fiscal_tipo,fiscal_perfil_id")
    .eq("user_id", empresaId)
    .order("position", { ascending: true })
    .limit(2000);
  if (error) throw new ErroFiscal(`Falha ao ler os serviços: ${error.message}`, 500);
  return ((data ?? []) as Array<Record<string, unknown>>).map((s) => ({
    id: String(s.id),
    titulo: texto(s.title) || "Serviço",
    preco: Number(s.price) || 0,
    ativo: s.active !== false,
    fiscalTipo: s.fiscal_tipo === "servico" ? "servico" : s.fiscal_tipo === "produto" ? "produto" : null,
    perfilId: texto(s.fiscal_perfil_id) || null,
  }));
}

type MudancaDeProduto = {
  id: string;
  fiscalTipo?: "mercadoria" | "servico" | null;
  ncm?: string | null;
  cest?: string | null;
  origem?: number | null;
  perfilId?: string | null;
};

async function perfisDaEmpresa(admin: Admin, empresaId: string) {
  const { data } = await admin.from("fiscal_perfis").select("id,tipo").eq("empresa_id", empresaId);
  return new Map(((data ?? []) as Array<{ id: string; tipo: string }>).map((p) => [p.id, p.tipo]));
}

export async function salvarProdutosFiscais(admin: Admin, empresaId: string, mudancas: MudancaDeProduto[]) {
  if (!Array.isArray(mudancas) || !mudancas.length) return { salvos: 0 };
  if (mudancas.length > 500) throw new ErroFiscal("Salve no máximo 500 produtos por vez.", 400);
  const perfis = await perfisDaEmpresa(admin, empresaId);

  const linhas = mudancas.map((mudanca) => {
    const linha: Record<string, unknown> = {};
    if ("fiscalTipo" in mudanca) {
      linha.fiscal_tipo = mudanca.fiscalTipo === "servico" ? "servico" : mudanca.fiscalTipo === "mercadoria" ? "mercadoria" : null;
    }
    if ("ncm" in mudanca) {
      const ncm = soDigitos(mudanca.ncm);
      if (ncm && ncm.length !== 8) throw new ErroFiscal(`NCM "${mudanca.ncm}" precisa ter 8 dígitos.`, 400);
      linha.ncm = ncm || null;
    }
    if ("cest" in mudanca) {
      const cest = soDigitos(mudanca.cest);
      if (cest && cest.length !== 7) throw new ErroFiscal(`CEST "${mudanca.cest}" precisa ter 7 dígitos.`, 400);
      linha.cest = cest || null;
    }
    if ("origem" in mudanca) {
      const origem = mudanca.origem === null || mudanca.origem === undefined ? null : Number(mudanca.origem);
      if (origem !== null && (!Number.isInteger(origem) || origem < 0 || origem > 8)) throw new ErroFiscal("Origem inválida.", 400);
      linha.fiscal_origem = origem;
    }
    if ("perfilId" in mudanca) {
      const perfilId = texto(mudanca.perfilId) || null;
      if (perfilId && !perfis.has(perfilId)) throw new ErroFiscal("Tributação escolhida não existe.", 400);
      linha.fiscal_perfil_id = perfilId;
    }
    return { id: texto(mudanca.id), linha };
  });

  let salvos = 0;
  for (let i = 0; i < linhas.length; i += 20) {
    const lote = linhas.slice(i, i + 20).filter((item) => item.id && Object.keys(item.linha).length);
    const resultados = await Promise.all(
      lote.map((item) => admin.from("products").update(item.linha).eq("id", item.id).eq("user_id", empresaId).select("id"))
    );
    for (const resultado of resultados) {
      if (resultado.error) throw new ErroFiscal(`Falha ao salvar: ${resultado.error.message}`, 500);
      salvos += resultado.data?.length ?? 0;
    }
  }
  return { salvos };
}

export async function salvarServicosFiscais(
  admin: Admin,
  empresaId: string,
  mudancas: Array<{ id: string; fiscalTipo?: "produto" | "servico" | null; perfilId?: string | null }>
) {
  if (!Array.isArray(mudancas) || !mudancas.length) return { salvos: 0 };
  const perfis = await perfisDaEmpresa(admin, empresaId);
  let salvos = 0;
  for (const mudanca of mudancas.slice(0, 500)) {
    const linha: Record<string, unknown> = {};
    if ("fiscalTipo" in mudanca) linha.fiscal_tipo = mudanca.fiscalTipo === "servico" ? "servico" : mudanca.fiscalTipo === "produto" ? "produto" : null;
    if ("perfilId" in mudanca) {
      const perfilId = texto(mudanca.perfilId) || null;
      if (perfilId && perfis.get(perfilId) !== "servico") throw new ErroFiscal("Escolha uma tributação de serviço.", 400);
      linha.fiscal_perfil_id = perfilId;
    }
    if (!Object.keys(linha).length) continue;
    const { data, error } = await admin.from("optional_services").update(linha).eq("id", texto(mudanca.id)).eq("user_id", empresaId).select("id");
    if (error) throw new ErroFiscal(`Falha ao salvar: ${error.message}`, 500);
    salvos += data?.length ?? 0;
  }
  return { salvos };
}
