import { calculateEffectiveQuantity } from "@/lib/price-tiers";

export type ShippingProviderId = "melhor_envio" | "superfrete" | "pickup";

/* A retirada no balcao entra na mesma lista das transportadoras: assim carrinho,
   checkout, pedido e portal do cliente continuam lendo um unico formato de
   frete escolhido, sem um segundo caminho paralelo so para retirada. */
export const PICKUP_PROVIDER_ID = "pickup" as const;
export const PICKUP_PROVIDER_LABEL = "Retirada no balcão";

export type PickupLocationSnapshot = {
  location_id: string;
  name: string;
  address_summary: string;
  instructions: string;
};

export type ShippingOption = {
  id: string;
  provider: ShippingProviderId;
  provider_label: string;
  name: string;
  service: string;
  price: number;
  delivery_time: number;
  logo_url?: string;
  currency?: string;
  /* Preenchido so nas opcoes de retirada, para o pedido guardar em qual balcao
     o cliente vai buscar sem depender de uma nova consulta. */
  pickup?: PickupLocationSnapshot;
};

export function isPickupShippingOption(option: { provider?: string | null } | null | undefined) {
  return String(option?.provider || "") === PICKUP_PROVIDER_ID;
}

export type ShippingProviderResult = {
  provider: ShippingProviderId;
  provider_label: string;
  configured: boolean;
  options: ShippingOption[];
  error?: string;
};

export type ShippingSelection = {
  destination_zip: string;
  option: ShippingOption;
  package_signature: string;
};

export type ProductionTimeUnit = "hours" | "days" | "business_days" | "weeks";

/**
 * Prazo de producao de um item convertido para dias uteis (dias de trabalho).
 *
 * A vitrine mistura unidades (horas, dias corridos, semanas) e a retirada
 * precisa de um numero so. Dias corridos viram uteis pela proporcao 5/7 e tudo
 * arredonda para cima: e melhor prometer um dia a mais do que atrasar.
 */
export function productionTimeToBusinessDays(
  value: number | null | undefined,
  unit: ProductionTimeUnit | string | null | undefined
) {
  const raw = Number(value);
  if (!Number.isFinite(raw) || raw <= 0) return 0;
  if (unit === "hours") return Math.ceil(raw / 24);
  if (unit === "weeks") return Math.ceil(raw * 5);
  if (unit === "days") return Math.ceil((raw * 5) / 7);
  return Math.ceil(raw);
}

/**
 * O pedido inteiro so fica pronto quando o item mais demorado fica, e os itens
 * sao produzidos em paralelo. Por isso o maximo, nunca a soma.
 */
export function resolveProductionBusinessDays(items: ShippingItemInput[]) {
  if (!Array.isArray(items) || items.length === 0) return 0;
  return items.reduce((maior, item) => {
    const dias = productionTimeToBusinessDays(item.production_time_value, item.production_time_unit);
    return dias > maior ? dias : maior;
  }, 0);
}

export type ShippingItemInput = {
  product_id?: string;
  /* Só para a mensagem de erro poder dizer qual produto esta incompleto. */
  name?: string | null;
  quantity: number;
  unit_type?: string | null;
  width_m?: number | null;
  height_m?: number | null;
  length_m?: number | null;
  shipping_weight_kg?: number | null;
  shipping_width_cm?: number | null;
  shipping_height_cm?: number | null;
  shipping_length_cm?: number | null;
  price_on_request?: boolean | null;
  /* Só a retirada usa: o prazo do balcão é somado ao tempo de produção. */
  production_time_value?: number | null;
  production_time_unit?: ProductionTimeUnit | string | null;
};

export type ShippingPackageMetrics = {
  weight: number;
  width: number;
  height: number;
  length: number;
  item_count: number;
};

export function sanitizePostalCode(value: unknown) {
  return String(value ?? "").replace(/\D/g, "").slice(0, 8);
}

export function formatShippingDeadline(days: number) {
  const safe = Math.max(0, Number(days) || 0);
  if (safe === 0) return "Hoje";
  if (safe === 1) return "1 dia útil";
  return `${safe} dias úteis`;
}

export function formatPickupDeadline(days: number) {
  const safe = Math.max(0, Number(days) || 0);
  if (safe === 0) return "Pronto para retirar";
  if (safe === 1) return "Pronto em 1 dia útil";
  return `Pronto em ${safe} dias úteis`;
}

export function formatShippingPrice(value: number) {
  const safe = Math.max(0, Number(value) || 0);
  if (safe <= 0) return "Grátis";
  return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(safe);
}

function round(value: number, decimals: number) {
  return Number(value.toFixed(decimals));
}

function readPositive(value: unknown) {
  const parsed = Number(value);
  if (!Number.isFinite(parsed) || parsed <= 0) return null;
  return parsed;
}

function resolveQuantityMultiplier(item: ShippingItemInput) {
  const unitType = item.unit_type || "unit";
  const quantity = Math.max(1, Number(item.quantity) || 1);
  const effectiveQuantity = calculateEffectiveQuantity({
    quantity,
    unit_type: unitType,
    width_m: Number(item.width_m) || 0,
    height_m: Number(item.height_m) || 0,
    length_m: Number(item.length_m) || 0,
  });
  if (!Number.isFinite(effectiveQuantity) || effectiveQuantity <= 0) return quantity;
  if (unitType === "square_meter" || unitType === "linear_meter" || unitType === "meter") {
    return effectiveQuantity;
  }
  return quantity;
}

export function buildShippingPackageFromItems(items: ShippingItemInput[]) {
  if (!Array.isArray(items) || items.length === 0) {
    return {
      ok: false as const,
      reason: "Carrinho vazio.",
      missing_fields: [] as string[],
      missing_items: [] as string[],
      packageMetrics: null,
      packageSignature: "",
    };
  }

  if (items.some((item) => item.price_on_request)) {
    return {
      ok: false as const,
      reason: "Itens sob consulta exigem confirmação manual de frete.",
      missing_fields: [] as string[],
      missing_items: [] as string[],
      packageMetrics: null,
      packageSignature: "",
    };
  }

  const relevantItems = items.filter((item) => Number(item.quantity || 0) > 0 && !item.price_on_request);
  if (!relevantItems.length) {
    return {
      ok: false as const,
      reason: "Itens sob consulta nao possuem frete automatico.",
      missing_fields: [] as string[],
      missing_items: [] as string[],
      packageMetrics: null,
      packageSignature: "",
    };
  }

  const missingFields = new Set<string>();
  const itensIncompletos = new Set<string>();
  let totalWeight = 0;
  /* Caixa do pedido: cabe o volume de todas as peças e nunca é menor que a maior
     peça em cada lado. Antes as peças eram empilhadas na altura, e 50 canetas de
     14 cm viravam uma caixa de 7 metros, que nenhuma transportadora cota. */
  let totalVolume = 0;
  let maiorLado = 0;
  let ladoDoMeio = 0;
  let menorLado = 0;

  relevantItems.forEach((item) => {
    const baseWeight = readPositive(item.shipping_weight_kg);
    const baseWidth = readPositive(item.shipping_width_cm);
    const baseHeight = readPositive(item.shipping_height_cm);
    const baseLength = readPositive(item.shipping_length_cm);

    if (baseWeight === null) missingFields.add("peso");
    if (baseWidth === null) missingFields.add("largura");
    if (baseHeight === null) missingFields.add("altura");
    if (baseLength === null) missingFields.add("comprimento");
    if (baseWeight === null || baseWidth === null || baseHeight === null || baseLength === null) {
      const nome = String(item.name || "").trim();
      if (nome) itensIncompletos.add(nome);
      return;
    }

    const multiplier = Math.max(1, resolveQuantityMultiplier(item));
    totalWeight += baseWeight * multiplier;
    const [lado1, lado2, lado3] = [baseWidth, baseHeight, baseLength].sort((x, y) => y - x);
    maiorLado = Math.max(maiorLado, lado1);
    ladoDoMeio = Math.max(ladoDoMeio, lado2);
    menorLado = Math.max(menorLado, lado3);
    totalVolume += baseWidth * baseHeight * baseLength * Math.max(1, Math.ceil(multiplier));
  });

  const ladoDoCubo = Math.cbrt(totalVolume);
  const packageLength = Math.max(maiorLado, ladoDoCubo);
  const packageWidth = Math.max(ladoDoMeio, packageLength > 0 ? Math.sqrt(totalVolume / packageLength) : 0);
  const packageHeight = Math.max(menorLado, packageLength * packageWidth > 0 ? totalVolume / (packageLength * packageWidth) : 0);

  if (missingFields.size > 0) {
    // Nomear o produto: com varios itens no carrinho, dizer apenas que
    // "faltam dados" nao permite descobrir qual deles esta incompleto.
    const nomes = Array.from(itensIncompletos);
    const alvo =
      nomes.length === 1
        ? `no produto "${nomes[0]}"`
        : nomes.length > 1
          ? `nos produtos: ${nomes.join(", ")}`
          : "neste pedido";
    return {
      ok: false as const,
      reason: `Faltam dados logísticos ${alvo} para cotar o frete.`,
      missing_fields: Array.from(missingFields),
      missing_items: nomes,
      packageMetrics: null,
      packageSignature: "",
    };
  }

  const packageMetrics: ShippingPackageMetrics = {
    weight: Math.max(0.1, round(totalWeight, 3)),
    width: Math.max(1, round(packageWidth, 2)),
    height: Math.max(1, round(packageHeight, 2)),
    length: Math.max(1, round(packageLength, 2)),
    item_count: relevantItems.length,
  };

  const packageSignature = [
    packageMetrics.weight,
    packageMetrics.width,
    packageMetrics.height,
    packageMetrics.length,
    packageMetrics.item_count,
  ].join(":");

  return {
    ok: true as const,
    reason: "",
    missing_fields: [] as string[],
    missing_items: [] as string[],
    packageMetrics,
    packageSignature,
  };
}
