/*
 * Estoque v2 — as leituras de tabela que as rotas do painel repetem: itens com
 * células, quem usa cada item e os atributos do produto. Sempre pelo cliente
 * de serviço e sempre com o filtro da empresa (`e.ids` / `e.dono`).
 */

import type { ContextoDoEstoque } from "./servidor";
import { gradeValida, rotuloDaCelula, chaveDaCelula } from "./regras";
import type { AtributoDoProduto, Celula, ItemDoEstoque, Papel, QuandoSai, UsoDoItem } from "./tipos";

type LinhaDoMaterial = {
  id: string;
  name: string;
  unit: string | null;
  papel: string | null;
  grade: unknown;
  min_stock_alert: number | null;
  waste_pct_default: number | null;
  permitir_negativo: boolean | null;
  conversoes: unknown;
  track_stock: boolean | null;
  cost_per_unit: number | null;
  current_stock: number | null;
  produto_id: string | null;
  is_active: boolean | null;
  purchase_format: string | null;
  is_service: boolean | null;
  roll_width_m: number | null;
  roll_length_m: number | null;
  package_qty: number | null;
  sheet_width_m: number | null;
  sheet_height_m: number | null;
};

type LinhaDaCelula = {
  id: string;
  item_id: string;
  valor1: string | null;
  valor2: string | null;
  chave: string;
  saldo: number | string;
  minimo: number | string | null;
  ativo: boolean;
};

const COLUNAS_DO_MATERIAL =
  "id,name,unit,papel,grade,min_stock_alert,waste_pct_default,permitir_negativo,conversoes,track_stock,cost_per_unit,current_stock,produto_id,is_active,purchase_format,is_service,roll_width_m,roll_length_m,package_qty,sheet_width_m,sheet_height_m";

export function celulaDaLinha(c: LinhaDaCelula, m: LinhaDoMaterial): Celula {
  return {
    celula_id: c.id,
    item_id: c.item_id,
    nome: m.name,
    unidade: m.unit || "un",
    papel: (m.papel === "pronto" ? "pronto" : "insumo") as Papel,
    valor1: c.valor1,
    valor2: c.valor2,
    rotulo: rotuloDaCelula(m.name, c.valor1, c.valor2),
    saldo: Number(c.saldo) || 0,
    minimo: c.minimo !== null && c.minimo !== undefined ? Number(c.minimo) : m.min_stock_alert !== null ? Number(m.min_stock_alert) : null,
    permite_negativo: m.permitir_negativo !== false,
    controla: m.track_stock !== false,
  };
}

function itemDaLinha(m: LinhaDoMaterial, celulas: Celula[]): ItemDoEstoque {
  return {
    id: m.id,
    nome: m.name,
    unidade: m.unit || "un",
    papel: m.papel === "pronto" ? "pronto" : "insumo",
    grade: gradeValida(m.grade),
    minimo: m.min_stock_alert !== null ? Number(m.min_stock_alert) : null,
    perda: Number(m.waste_pct_default) || 0,
    permitir_negativo: m.permitir_negativo !== false,
    conversoes: Array.isArray(m.conversoes) ? (m.conversoes as ItemDoEstoque["conversoes"]) : [],
    controla: m.track_stock !== false,
    custo_medio: Number(m.cost_per_unit) || 0,
    saldo: Number(m.current_stock) || 0,
    produto_id: m.produto_id,
    ativo: m.is_active !== false,
    formato_de_compra: m.purchase_format,
    servico: m.is_service === true,
    compra: {
      roll_width_m: m.roll_width_m,
      roll_length_m: m.roll_length_m,
      package_qty: m.package_qty,
      sheet_width_m: m.sheet_width_m,
      sheet_height_m: m.sheet_height_m,
    },
    celulas,
  };
}

/** Itens do estoque da empresa, com as células ativas (e as inativas com saldo). */
export async function lerItens(
  e: ContextoDoEstoque,
  filtro: { papel?: Papel | null; ids?: string[] | null; comUsos?: boolean } = {}
): Promise<ItemDoEstoque[]> {
  let q = e.admin.from("materials").select(COLUNAS_DO_MATERIAL).in("user_id", e.ids).order("name");
  if (filtro.papel) q = q.eq("papel", filtro.papel);
  if (filtro.ids) {
    if (!filtro.ids.length) return [];
    q = q.in("id", filtro.ids.slice(0, 500));
  }
  const { data: materiais } = await q.limit(2000);
  const lista = (materiais as LinhaDoMaterial[] | null) ?? [];
  if (!lista.length) return [];

  const porItem = new Map<string, Celula[]>();
  const ids = lista.map((m) => m.id);
  for (let i = 0; i < ids.length; i += 200) {
    const { data: celulas } = await e.admin
      .from("estoque_celulas")
      .select("id,item_id,valor1,valor2,chave,saldo,minimo,ativo")
      .eq("empresa_id", e.dono)
      .in("item_id", ids.slice(i, i + 200))
      .limit(5000);
    for (const c of (celulas as LinhaDaCelula[] | null) ?? []) {
      if (!c.ativo && Number(c.saldo) === 0) continue;
      const m = lista.find((x) => x.id === c.item_id);
      if (!m) continue;
      const arr = porItem.get(c.item_id) ?? [];
      arr.push(celulaDaLinha(c, m));
      porItem.set(c.item_id, arr);
    }
  }

  const itens = lista.map((m) => {
    const grade = gradeValida(m.grade);
    const ordem = new Map<string, number>();
    const v1 = grade[0]?.valores ?? [];
    const v2 = grade[1]?.valores ?? [null];
    let n = 0;
    for (const a of v1) for (const b of v2) ordem.set(chaveDaCelula(a, b), n++);
    const celulas = (porItem.get(m.id) ?? []).sort(
      (a, b) => (ordem.get(chaveDaCelula(a.valor1, a.valor2)) ?? 9999) - (ordem.get(chaveDaCelula(b.valor1, b.valor2)) ?? 9999)
    );
    return itemDaLinha(m, celulas);
  });

  if (filtro.comUsos) await juntarUsos(e, itens);
  return itens;
}

/** Quem usa cada item: produto que sai dele (pronto) e composições, receitas e serviços. */
export async function juntarUsos(e: ContextoDoEstoque, itens: ItemDoEstoque[]) {
  if (!itens.length) return;
  const ids = itens.map((i) => i.id);
  const [{ data: prontos }, { data: linhas }, { data: receitas }] = await Promise.all([
    e.admin.from("products").select("id,name,estoque_item_id").in("user_id", e.ids).in("estoque_item_id", ids).limit(2000),
    e.admin
      .from("product_materials")
      .select("material_id,quando_sai,product_id,products!inner(id,name,user_id)")
      .in("material_id", ids)
      .in("products.user_id", e.ids)
      .limit(5000),
    e.admin
      .from("product_option_recipes")
      .select("material_id,product_id,active,products!inner(id,name,user_id)")
      .in("material_id", ids)
      .in("products.user_id", e.ids)
      .limit(2000),
  ]);
  const usos = new Map<string, UsoDoItem[]>();
  const somar = (item: string, u: UsoDoItem) => {
    const arr = usos.get(item) ?? [];
    if (!arr.some((x) => x.produto_id === u.produto_id && x.como === u.como)) arr.push(u);
    usos.set(item, arr);
  };
  for (const p of (prontos as Array<{ id: string; name: string; estoque_item_id: string }> | null) ?? []) {
    somar(p.estoque_item_id, { produto_id: p.id, produto: p.name, como: "pronto" });
  }
  for (const l of (linhas as unknown as Array<{ material_id: string; quando_sai: QuandoSai | null; product_id: string; products: { name: string } }> | null) ?? []) {
    somar(l.material_id, { produto_id: l.product_id, produto: l.products?.name ?? "Produto", como: "composicao", quando_sai: l.quando_sai });
  }
  for (const r of (receitas as unknown as Array<{ material_id: string; product_id: string; active: boolean; products: { name: string } }> | null) ?? []) {
    if (r.active === false) continue;
    somar(r.material_id, { produto_id: r.product_id, produto: r.products?.name ?? "Produto", como: "receita" });
  }
  for (const i of itens) i.usos = (usos.get(i.id) ?? []).sort((a, b) => a.produto.localeCompare(b.produto, "pt-BR"));
}

/** Atributos do produto com as opções (id e rótulo), na ordem da tela. */
export async function lerAtributos(e: ContextoDoEstoque, produtoId: string): Promise<AtributoDoProduto[]> {
  const { data } = await e.admin
    .from("product_attributes")
    .select("id,name,options,allow_multiple,position")
    .eq("product_id", produtoId)
    .order("position", { ascending: true, nullsFirst: false });
  return ((data as Array<{ id: string; name: string; options: unknown; allow_multiple: boolean | null }> | null) ?? []).map((a) => ({
    id: a.id,
    name: a.name,
    allow_multiple: a.allow_multiple === true,
    options: (Array.isArray(a.options) ? (a.options as Array<{ id?: string; label?: string; position?: number }>) : [])
      .slice()
      .sort((x, y) => (Number(x.position) || 0) - (Number(y.position) || 0))
      .map((o) => ({ id: o.id, label: String(o.label ?? "") }))
      .filter((o) => o.label.trim() !== ""),
  }));
}

/** Uma célula pelo id, com o item (para as respostas que só trazem o id). */
export async function lerCelulas(e: ContextoDoEstoque, celulaIds: string[]): Promise<Map<string, Celula>> {
  const mapa = new Map<string, Celula>();
  const ids = Array.from(new Set(celulaIds.filter(Boolean)));
  if (!ids.length) return mapa;
  const { data: celulas } = await e.admin
    .from("estoque_celulas")
    .select("id,item_id,valor1,valor2,chave,saldo,minimo,ativo")
    .eq("empresa_id", e.dono)
    .in("id", ids.slice(0, 500));
  const linhas = (celulas as LinhaDaCelula[] | null) ?? [];
  const itens = Array.from(new Set(linhas.map((c) => c.item_id)));
  if (!itens.length) return mapa;
  const { data: materiais } = await e.admin.from("materials").select(COLUNAS_DO_MATERIAL).in("id", itens);
  for (const c of linhas) {
    const m = ((materiais as LinhaDoMaterial[] | null) ?? []).find((x) => x.id === c.item_id);
    if (m) mapa.set(c.id, celulaDaLinha(c, m));
  }
  return mapa;
}
