import { NextResponse } from "next/server";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { numeroDoTexto } from "@/lib/numero-do-campo";

type Params = { params: Promise<{ id: string; materialId: string }> };

function normalizeString(value: unknown) {
  return typeof value === "string" ? value.trim() : "";
}

function inteiroOuNulo(value: unknown) {
  if (value === null || value === undefined || value === "") return null;
  const n = typeof value === "number" ? value : numeroDoTexto(String(value));
  if (!Number.isFinite(n) || n < 0) return null;
  return Math.trunc(n);
}

async function getAuth(permission: "suppliers.read" | "suppliers.list.write") {
  const supabase = await createClient();
  const {
    data: { user },
    error,
  } = await supabase.auth.getUser();
  if (error || !user) return { ok: false as const, status: 401, user: null, ctx: null };
  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, permission)) return { ok: false as const, status: 403, user, ctx: null };
  return { ok: true as const, status: 200, user, ctx };
}

async function insertActivity(
  admin: ReturnType<typeof createAdminClient>,
  companyId: string,
  supplierId: string,
  actorUserId: string | null,
  description: string,
  payload: Record<string, unknown>,
  action = "material.updated"
) {
  await admin.from("supplier_activity_log").insert({
    company_id: companyId,
    supplier_id: supplierId,
    entity_type: "supplier",
    entity_id: supplierId,
    action,
    description,
    payload,
    actor_user_id: actorUserId,
  });
}

export async function PATCH(req: Request, { params }: Params) {
  const auth = await getAuth("suppliers.list.write");
  if (!auth.ok || !auth.ctx) {
    return NextResponse.json({ error: auth.status === 401 ? "Não autenticado." : "Sem permissão." }, { status: auth.status });
  }

  const { id, materialId } = await params;
  const body = (await req.json().catch(() => null)) as Record<string, unknown> | null;
  const name = normalizeString(body?.name);
  const unit = normalizeString(body?.unit) || "un";
  const notes = normalizeString(body?.notes) || null;
  const sku = normalizeString(body?.sku) || null;
  const purchaseUnit = normalizeString(body?.purchaseUnit) || null;
  const leadTimeDays = inteiroOuNulo(body?.leadTimeDays);
  const preferred = body?.preferred === true;
  /* `linkedMaterialId` ausente no corpo significa "nao mexe no vinculo";
     string vazia significa "desliga". Sao coisas diferentes. */
  const mexeNoVinculo = Object.prototype.hasOwnProperty.call(body ?? {}, "linkedMaterialId");
  const linkedMaterialId = mexeNoVinculo ? normalizeString(body?.linkedMaterialId) || null : undefined;

  if (!name) {
    return NextResponse.json({ error: "Nome do material é obrigatório." }, { status: 400 });
  }

  const admin = createAdminClient();
  const { data: material, error: materialError } = await admin
    .from("supplier_materials")
    .select("id,name,unit,notes,sku,linked_material_id")
    .eq("company_id", auth.ctx.companyId)
    .eq("supplier_id", id)
    .eq("id", materialId)
    .is("deleted_at", null)
    .maybeSingle();

  if (materialError) return NextResponse.json({ error: "Falha ao localizar material." }, { status: 500 });
  if (!material?.id) return NextResponse.json({ error: "Material não encontrado." }, { status: 404 });

  /* `%` e `_` sao curinga no ilike: um material "Tinta 100%" virava padrao de
     busca e podia acusar conflito que nao existe. */
  const nomeSemCuringa = name.replace(/[%_\\]/g, (c) => `\\${c}`);

  const { data: conflict } = await admin
    .from("supplier_materials")
    .select("id")
    .eq("company_id", auth.ctx.companyId)
    .eq("supplier_id", id)
    .ilike("name", nomeSemCuringa)
    .neq("id", materialId)
    .is("deleted_at", null)
    .maybeSingle();

  if (conflict?.id) {
    return NextResponse.json({ error: "Já existe outro material com este nome para o fornecedor." }, { status: 400 });
  }

  if (linkedMaterialId) {
    const { data: insumo } = await admin
      .from("materials")
      .select("id")
      .eq("user_id", auth.ctx.companyId)
      .eq("id", linkedMaterialId)
      .maybeSingle();
    if (!insumo?.id) {
      return NextResponse.json({ error: "Esse insumo não é da sua empresa." }, { status: 400 });
    }
  }

  const mudancas: Record<string, unknown> = {
    name,
    unit,
    notes,
    sku,
    purchase_unit: purchaseUnit,
    lead_time_days: leadTimeDays,
    preferred,
  };
  if (linkedMaterialId !== undefined) mudancas.linked_material_id = linkedMaterialId;

  /* Preferido e um por insumo. Tirar o anterior antes evita o indice unico
     recusar a gravacao com uma mensagem que ninguem entenderia. */
  const insumoDoPreferido = linkedMaterialId !== undefined ? linkedMaterialId : material.linked_material_id;
  if (preferred && insumoDoPreferido) {
    await admin
      .from("supplier_materials")
      .update({ preferred: false })
      .eq("company_id", auth.ctx.companyId)
      .eq("linked_material_id", insumoDoPreferido)
      .neq("id", materialId)
      .is("deleted_at", null);
  }

  const { error: updateError } = await admin
    .from("supplier_materials")
    .update(mudancas)
    .eq("company_id", auth.ctx.companyId)
    .eq("supplier_id", id)
    .eq("id", materialId);

  if (updateError) {
    const duplicado = String((updateError as { code?: string }).code || "") === "23505";
    return NextResponse.json(
      { error: duplicado ? "Esse insumo já está ligado a outro item deste fornecedor." : "Falha ao atualizar material." },
      { status: duplicado ? 400 : 500 }
    );
  }

  await insertActivity(admin, auth.ctx.companyId, id, auth.user?.id ?? null, `Material ${name} atualizado no fornecedor.`, {
    materialId,
    name,
    unit,
    notes,
    sku,
  });

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

export async function DELETE(_: Request, { params }: Params) {
  const auth = await getAuth("suppliers.list.write");
  if (!auth.ok || !auth.ctx) {
    return NextResponse.json({ error: auth.status === 401 ? "Não autenticado." : "Sem permissão." }, { status: auth.status });
  }

  const { id, materialId } = await params;
  const admin = createAdminClient();

  const { data: material, error: materialError } = await admin
    .from("supplier_materials")
    .select("id,name")
    .eq("company_id", auth.ctx.companyId)
    .eq("supplier_id", id)
    .eq("id", materialId)
    .is("deleted_at", null)
    .maybeSingle();

  if (materialError) return NextResponse.json({ error: "Falha ao localizar material." }, { status: 500 });
  if (!material?.id) return NextResponse.json({ error: "Material não encontrado." }, { status: 404 });

  const { error: deleteError } = await admin
    .from("supplier_materials")
    .update({ deleted_at: new Date().toISOString() })
    .eq("company_id", auth.ctx.companyId)
    .eq("supplier_id", id)
    .eq("id", materialId);

  if (deleteError) {
    return NextResponse.json({ error: "Falha ao excluir material." }, { status: 500 });
  }

  await admin
    .from("supplier_purchase_order_items")
    .update({ material_id: null })
    .eq("company_id", auth.ctx.companyId)
    .eq("supplier_id", id)
    .eq("material_id", materialId);

  await insertActivity(
    admin,
    auth.ctx.companyId,
    id,
    auth.user?.id ?? null,
    `Material ${material.name} removido do fornecedor.`,
    { materialId, name: material.name },
    /* Dizia "material.updated" para uma exclusao: no historico, um item
       removido aparecia como atualizado. */
    "material.deleted"
  );

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