import { NextResponse } from "next/server";
import { createClient } from "@/utils/supabase/server";
import { gerarRascunhoOrcamento } from "@/lib/iaService";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { calculateEffectiveQuantity, checkSaleQuantityLimits, resolveTierPrice, type ProductPriceTier } from "@/lib/price-tiers";
import { valorMinimoDoItem, COLUNAS_DO_VALOR_MINIMO } from "@/lib/adicionais";

const DEFAULT_MAX_TENTATIVAS_DIA = 20;
const QUICK_PLACEHOLDER_NAME = "cliente pendente";
const QUICK_PLACEHOLDER_COMPANY = "orcamento rapido";
const QUICK_PLACEHOLDER_NOTES = "Cliente técnico criado automaticamente para orçamentos rápidos sem cliente definido.";

interface ClientRow {
  id: string;
  name: string;
  company_name: string | null;
}

interface NormalizedItem {
  product_id: string | null;
  unit_type: string;
  width_m: number | null;
  height_m: number | null;
  length_m: number | null;
  title: string;
  description: string | null;
  quantity: number;
  unit_price: number;
  unit_cost: number;
  line_total: number;
  valor_minimo_aplicado: number | null;
  valor_sem_minimo: number | null;
}

interface ProductRow {
  id: string;
  name: string;
  price: number;
  valor_minimo_item?: number | null;
  valor_minimo_orcamentos?: boolean | null;
  price_on_request?: boolean | null;
  unit_type: string | null;
  description?: string | null;
  product_price_tiers?: ProductPriceTier[] | null;
  min_sale_qty_enabled?: boolean | null;
  min_sale_qty?: number | null;
  max_sale_qty_enabled?: boolean | null;
  max_sale_qty?: number | null;
  allowed_sale_qty_enabled?: boolean | null;
  allowed_sale_quantities?: number[] | null;
}

function getTodayIsoDate() {
  return new Date().toISOString().slice(0, 10);
}

function addDays(date: Date, days: number) {
  const d = new Date(date);
  d.setDate(d.getDate() + days);
  return d;
}

function toIsoDate(date: Date) {
  return date.toISOString().slice(0, 10);
}

function formatQuantityValue(value: number | null | undefined) {
  const numeric = Number(value);
  if (!Number.isFinite(numeric)) return "0";
  const rounded = Number(numeric.toFixed(3));
  return Number.isInteger(rounded)
    ? String(rounded)
    : new Intl.NumberFormat("pt-BR", { minimumFractionDigits: 0, maximumFractionDigits: 3 }).format(rounded);
}

function parsePrazoEntregaIsoDate(text: string, todayIso: string) {
  const raw = normalizeForMatch(text);
  if (!raw.includes("prazo") && !raw.includes("entrega")) return null;

  if (raw.includes("hoje")) return todayIso;

  const today = new Date(`${todayIso}T00:00:00.000Z`);
  if (!Number.isFinite(today.getTime())) return null;

  if (raw.includes("amanha") || raw.includes("amanhã")) return toIsoDate(addDays(today, 1));

  const weekdays: Array<{ keys: string[]; day: number }> = [
    { keys: ["domingo"], day: 0 },
    { keys: ["segunda", "segunda-feira", "segunda feira"], day: 1 },
    { keys: ["terca", "terça", "terca-feira", "terça-feira", "terca feira", "terça feira"], day: 2 },
    { keys: ["quarta", "quarta-feira", "quarta feira"], day: 3 },
    { keys: ["quinta", "quinta-feira", "quinta feira"], day: 4 },
    { keys: ["sexta", "sexta-feira", "sexta feira"], day: 5 },
    { keys: ["sabado", "sábado", "sabado-feira", "sábado-feira"], day: 6 },
  ];

  const matched = weekdays.find((w) => w.keys.some((k) => raw.includes(k)));
  if (!matched) return null;

  const currentDow = today.getUTCDay();
  let delta = (matched.day - currentDow + 7) % 7;
  if (delta === 0) delta = 7;
  return toIsoDate(addDays(today, delta));
}

function getMaxTentativasDia() {
  const raw = process.env.IA_MAX_TENTATIVAS_DIA;
  const n = raw ? Number(raw) : DEFAULT_MAX_TENTATIVAS_DIA;
  if (!Number.isFinite(n) || n <= 0) return DEFAULT_MAX_TENTATIVAS_DIA;
  return Math.floor(n);
}

function normalizeForMatch(value: string) {
  return (value ?? "")
    .toString()
    .normalize("NFD")
    .replace(/\p{Diacritic}/gu, "")
    .toLowerCase()
    .replace(/\s+/g, " ")
    .trim();
}

function cleanClientQuery(value: string) {
  const v = normalizeForMatch(value);
  return v.replace(/^cliente\s+/, "").trim();
}

function pickBestClientId(
  rawQuery: string,
  clients: Array<{ id: string; name: string; company_name: string | null }>
) {
  const query = cleanClientQuery(rawQuery);
  if (!query) return null;

  let best: { id: string; score: number } | null = null;

  for (const c of clients) {
    const candidate = normalizeForMatch(`${c.company_name ?? ""} ${c.name}`);
    if (!candidate) continue;

    let score = 0;
    if (candidate === query) score = 100;
    else if (candidate.startsWith(query)) score = 90;
    else if (candidate.includes(query)) score = 70;
    else if (query.includes(candidate) && candidate.length >= 4) score = 60;

    if (!best || score > best.score) best = { id: c.id, score };
  }

  if (!best || best.score < 60) return null;
  return best.id;
}

function tokenize(value: string) {
  return normalizeForMatch(value)
    .split(" ")
    .map((t) => t.trim())
    .filter((t) => t.length >= 2);
}

function pickBestProduct(
  title: string,
  products: ProductRow[]
): ProductRow | null {
  const q = normalizeForMatch(title);
  if (!q) return null;
  const qTokens = tokenize(q);
  let best: { p: ProductRow; score: number } | null = null;
  for (const p of products) {
    const cand = normalizeForMatch(p.name);
    const candTokens = tokenize(cand);
    let score = 0;
    if (cand === q) score = 100;
    else if (cand.startsWith(q)) score = 90;
    else if (cand.includes(q)) score = 80;
    else if (q.includes(cand) && cand.length >= 4) score = 70;

    if (score < 80 && qTokens.length > 0 && candTokens.length > 0) {
      const overlap = qTokens.filter((t) => candTokens.includes(t)).length;
      const overlapPct = overlap / Math.max(1, qTokens.length);
      const tokenScore = Math.round(overlapPct * 75);
      score = Math.max(score, tokenScore);
    }

    if (!best || score > best.score) best = { p, score };
  }
  if (!best || best.score < 35) return null;
  return best.p;
}

function isMissingColumnError(error: unknown, columnName: string) {
  if (!error || typeof error !== "object") return false;
  const message =
    typeof (error as { message?: unknown }).message === "string"
      ? ((error as { message?: string }).message || "").toLowerCase()
      : "";
  return message.includes(columnName.toLowerCase());
}

async function ensureQuickPlaceholderClient(
  supabase: Awaited<ReturnType<typeof createClient>>,
  tenantId: string
) {
  const selectColumns = "id,user_id,name,company_name";
  let supportsFlagColumn = true;

  const findExisting = async () => {
    if (!supportsFlagColumn) {
      return supabase
        .from("clients")
        .select(selectColumns)
        .eq("user_id", tenantId)
        .eq("name", QUICK_PLACEHOLDER_NAME)
        .eq("company_name", QUICK_PLACEHOLDER_COMPANY)
        .order("created_at", { ascending: true })
        .limit(1)
        .maybeSingle();
    }

    return supabase
      .from("clients")
      .select(selectColumns)
      .eq("user_id", tenantId)
      .eq("is_system_placeholder", true)
      .order("created_at", { ascending: true })
      .limit(1)
      .maybeSingle();
  };

  let { data: existing, error: existingErr } = await findExisting();
  if (existingErr && isMissingColumnError(existingErr, "is_system_placeholder")) {
    supportsFlagColumn = false;
    const retry = await findExisting();
    existing = retry.data;
    existingErr = retry.error;
  }

  if (existingErr) throw existingErr;
  if (existing) return existing;

  const now = new Date().toISOString();
  let insertPayload: Record<string, unknown> = {
    user_id: tenantId,
    name: QUICK_PLACEHOLDER_NAME,
    company_name: QUICK_PLACEHOLDER_COMPANY,
    type: "pf",
    active: false,
    notes: QUICK_PLACEHOLDER_NOTES,
    created_at: now,
    updated_at: now,
  };

  if (supportsFlagColumn) insertPayload.is_system_placeholder = true;

  let finalCreated: Record<string, unknown> | null = null;
  let finalError: unknown = null;
  for (let attempt = 0; attempt < 4; attempt++) {
    const insertion = await supabase.from("clients").insert([insertPayload]).select(selectColumns).single();
    finalCreated = insertion.data as Record<string, unknown> | null;
    finalError = insertion.error;
    if (!finalError) break;

    const optionalColumn = ["is_system_placeholder", "active", "notes"].find(
      (column) => Object.prototype.hasOwnProperty.call(insertPayload, column) && isMissingColumnError(finalError, column)
    );

    if (!optionalColumn) break;
    if (optionalColumn === "is_system_placeholder") supportsFlagColumn = false;
    const fallbackPayload = { ...insertPayload };
    delete fallbackPayload[optionalColumn];
    insertPayload = fallbackPayload;
  }

  if (finalError) {
    const code =
      typeof finalError === "object" && finalError !== null && "code" in finalError
        ? (finalError as { code?: string }).code
        : undefined;

    if (code === "23505") {
      const retry = await findExisting();
      if (retry.error) throw retry.error;
      if (retry.data) return retry.data;
    }
    throw finalError;
  }

  return finalCreated;
}

async function getNextQuoteNumber(supabase: Awaited<ReturnType<typeof createClient>>, userId: string) {
  const { data, error } = await supabase
    .from("quotes")
    .select("number")
    .eq("user_id", userId)
    .order("number", { ascending: false })
    .limit(1);
  if (error) throw error;
  const currentMax = data && data.length > 0 ? Number(data[0].number) : 0;
  return currentMax + 1;
}

async function getQuoteNumberStartFrom(supabase: Awaited<ReturnType<typeof createClient>>, userId: string) {
  const { data, error } = await supabase
    .from("profiles")
    .select("quote_number_start_from")
    .eq("id", userId)
    .maybeSingle();

  if (error) {
    if (error.code === "PGRST116" || isMissingColumnError(error, "quote_number_start_from")) return 1;
    throw error;
  }

  const raw = (data as { quote_number_start_from?: unknown } | null)?.quote_number_start_from;
  const parsed = Number(raw);
  if (!Number.isFinite(parsed)) return 1;
  return Math.max(1, Math.floor(parsed));
}

async function getNextQuoteNumberWithFloor(
  supabase: Awaited<ReturnType<typeof createClient>>,
  userId: string,
  startFrom: number
) {
  const sequential = await getNextQuoteNumber(supabase, userId);
  return Math.max(sequential, Math.max(1, Math.floor(startFrom || 1)));
}

export async function POST(req: Request) {
  try {
    const supabase = await createClient();
    const {
      data: { user },
      error: userError,
    } = await supabase.auth.getUser();

    if (userError || !user) {
      return NextResponse.json({ error: "Não autenticado." }, { status: 401 });
    }

    const ctx = await getCompanyContext(supabase, user);
    const writeUserId = ctx.ownerId ?? ctx.companyId;
    const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
    if (!canCompany(ctx, user.id, "ia.write") || !canCompany(ctx, user.id, "quotes.write")) {
      return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
    }

    const body = (await req.json().catch(() => null)) as
      | {
          instruction?: unknown;
          imageDataUrl?: unknown;
        }
      | null;

    const instruction = typeof body?.instruction === "string" ? body.instruction.trim() : "";
    const imageDataUrl = typeof body?.imageDataUrl === "string" ? body.imageDataUrl.trim() : "";
    if (!instruction && !imageDataUrl) {
      return NextResponse.json({ error: "Informe um texto ou cole uma imagem para gerar o orçamento." }, { status: 400 });
    }

    const hoje = getTodayIsoDate();
    const maxTentativasDia = getMaxTentativasDia();

    const { data: consumoAtual, error: consumoSelectError } = await supabase
      .from("ia_consumo")
      .select("tentativas")
      .eq("user_id", writeUserId)
      .eq("data", hoje)
      .maybeSingle();

    if (consumoSelectError) {
      return NextResponse.json({ error: "Falha ao verificar consumo de IA." }, { status: 500 });
    }

    const tentativasAtuais = consumoAtual?.tentativas ?? 0;
    if (tentativasAtuais >= maxTentativasDia) {
      return NextResponse.json({ error: "Limite diário de tentativas de IA atingido." }, { status: 429 });
    }

    const { error: consumoWriteError } =
      tentativasAtuais === 0
        ? await supabase.from("ia_consumo").insert({
            user_id: writeUserId,
            data: hoje,
            tentativas: 1,
          })
        : await supabase
            .from("ia_consumo")
            .update({ tentativas: tentativasAtuais + 1 })
            .eq("user_id", writeUserId)
            .eq("data", hoje);

    if (consumoWriteError) {
      return NextResponse.json({ error: "Falha ao registrar consumo de IA." }, { status: 500 });
    }

    const draft = await gerarRascunhoOrcamento(
      {
        instruction,
        imageDataUrl: imageDataUrl || null,
      },
      hoje
    );

    const { data: clientsData, error: clientsError } = await supabase
      .from("clients")
      .select("id,name,company_name")
      .in("user_id", tenantIds)
      .eq("active", true);
    if (clientsError) throw clientsError;

    let clientId = pickBestClientId(draft.client_query, (clientsData as ClientRow[]) || []);
    if (!clientId) {
      try {
        const quickClient = await ensureQuickPlaceholderClient(supabase, writeUserId);
        clientId = String((quickClient as { id?: unknown } | null)?.id || "");
      } catch (err) {
        const message = err instanceof Error ? err.message : "Nao foi possivel preparar cliente tecnico.";
        return NextResponse.json({ error: message }, { status: 400 });
      }
    }

    if (!clientId) {
      return NextResponse.json({ error: "Nao foi possivel definir um cliente para o orçamento." }, { status: 400 });
    }

    let productsData: ProductRow[] | null = null;
    let productsError: { message?: string } | null = null;

    const productsWithTiers = await supabase
      .from("products")
      .select(`id,name,description,price,price_on_request,unit_type,min_sale_qty_enabled,min_sale_qty,max_sale_qty_enabled,max_sale_qty,allowed_sale_qty_enabled,allowed_sale_quantities,product_price_tiers(min_qty,max_qty,unit_price,sort_order,active),${COLUNAS_DO_VALOR_MINIMO}`)
      .in("user_id", tenantIds)
      .neq("product_kind", "digital")
      .eq("active", true);

    productsData = (productsWithTiers.data as ProductRow[] | null) ?? null;
    productsError = (productsWithTiers.error as { message?: string } | null) ?? null;
    if (productsError) {
      const message = String(productsError?.message ?? "").toLowerCase();
      if (message.includes("product_price_tiers")) {
        const fallback = await supabase
          .from("products")
          .select("id,name,description,price,price_on_request,unit_type,min_sale_qty_enabled,min_sale_qty,max_sale_qty_enabled,max_sale_qty,allowed_sale_qty_enabled,allowed_sale_quantities")
          .in("user_id", tenantIds)
          .neq("product_kind", "digital")
          .eq("active", true);
        productsData = (fallback.data as ProductRow[] | null) ?? null;
        productsError = (fallback.error as { message?: string } | null) ?? null;
      }
    }
    if (productsError) throw productsError;
    const products = (productsData as ProductRow[]) || [];

    const quantityRuleErrors: string[] = [];
    const manualItemWarnings: string[] = [];
    const normalizedItems: NormalizedItem[] = (draft.items || [])
      .map((it): NormalizedItem | null => {
        const unitTypeRaw = typeof it.unit_type === "string" ? it.unit_type : "";
        const unitType = [
          "unit",
          "square_meter",
          "linear_meter",
          "meter",
          "box",
          "ream",
        ].includes(unitTypeRaw)
          ? unitTypeRaw
          : "unit";

        const width_m = typeof it.width_m === "number" && it.width_m > 0 ? it.width_m : 0;
        const height_m = typeof it.height_m === "number" && it.height_m > 0 ? it.height_m : 0;
        const length_m = typeof it.length_m === "number" && it.length_m > 0 ? it.length_m : 0;

        const quantity = typeof it.quantity === "number" ? Math.max(1, it.quantity) : 1;
        let unit_price = typeof it.unit_price === "number" ? Math.max(0, it.unit_price) : 0;
        const unit_cost = typeof it.unit_cost === "number" ? Math.max(0, it.unit_cost) : 0;
        let title = typeof it.title === "string" ? it.title.trim() : "";
        let description = typeof it.description === "string" ? it.description.trim() : null;

        if (!title) return null;

        const originalTitle = title;
        const matched = pickBestProduct(title, products);
        const product_id = matched ? matched.id : null;
        const finalUnitType = product_id && matched?.unit_type ? matched.unit_type : unitType;
        const normalizedQuantity = calculateEffectiveQuantity({
          quantity,
          unit_type: finalUnitType || "unit",
          width_m,
          height_m,
          length_m,
        });
        if (matched) {
          const quantityCheck = checkSaleQuantityLimits({
            min_sale_qty_enabled: matched.min_sale_qty_enabled,
            min_sale_qty: matched.min_sale_qty,
            max_sale_qty_enabled: matched.max_sale_qty_enabled,
            max_sale_qty: matched.max_sale_qty,
            allowed_sale_qty_enabled: matched.allowed_sale_qty_enabled,
            allowed_sale_quantities: matched.allowed_sale_quantities,
            effectiveQuantity: normalizedQuantity,
          });
          if (!quantityCheck.isValid) {
            if (quantityCheck.isBelowMin && quantityCheck.minQty !== null) {
              quantityRuleErrors.push(
                `Item "${title}": quantidade mínima ${formatQuantityValue(quantityCheck.minQty)}.`
              );
            } else if (quantityCheck.isAboveMax && quantityCheck.maxQty !== null) {
              quantityRuleErrors.push(
                `Item "${title}": quantidade máxima ${formatQuantityValue(quantityCheck.maxQty)}.`
              );
            } else if (quantityCheck.isNotAllowedValue && quantityCheck.allowedQuantities.length > 0) {
              const values = quantityCheck.allowedQuantities.map((value) => formatQuantityValue(value)).join(", ");
              quantityRuleErrors.push(`Item "${title}": use uma quantidade permitida (${values}).`);
            }
            return null;
          }
        }
        // O mínimo do item só entra quando o preço sai do produto (a IA não deu preço).
        let minimoDoProduto: number | null = null;
        if (product_id && (!unit_price || unit_price <= 0)) {
          minimoDoProduto = valorMinimoDoItem(matched, "orcamentos");
          if (matched?.price_on_request) {
            unit_price = 0;
          } else {
            const resolved = resolveTierPrice(
              Number(matched?.price) || 0,
              normalizedQuantity,
              (matched?.product_price_tiers as ProductPriceTier[] | null | undefined) || []
            );
            unit_price = Math.max(0, resolved.unitPrice);
          }
        }
        if (product_id && matched?.name) {
          title = matched.name;
        }
        if (product_id && !description && matched?.description) {
          description = matched.description;
        }
        if (!product_id) {
          manualItemWarnings.push(originalTitle);
          description = [
            description || "",
            "Item manual criado pela IA. Revise produto, descricao, medidas e preco antes de aprovar.",
          ]
            .filter(Boolean)
            .join(" ");
        }

        const quantidadeEfetiva = calculateEffectiveQuantity({
          quantity,
          unit_type: finalUnitType || "unit",
          width_m,
          height_m,
          length_m,
        });
        let line_total = quantidadeEfetiva * unit_price;
        let valor_minimo_aplicado: number | null = null;
        let valor_sem_minimo: number | null = null;
        if (minimoDoProduto !== null && Math.round(line_total * 100) / 100 < minimoDoProduto) {
          valor_sem_minimo = Math.round(line_total * 100) / 100;
          valor_minimo_aplicado = minimoDoProduto;
          line_total = minimoDoProduto;
          unit_price = Math.round((minimoDoProduto / (quantidadeEfetiva > 0 ? quantidadeEfetiva : 1)) * 1e6) / 1e6;
        }

        return {
          product_id,
          unit_type: finalUnitType || "unit",
          width_m: width_m > 0 ? width_m : null,
          height_m: height_m > 0 ? height_m : null,
          length_m: length_m > 0 ? length_m : null,
          title,
          description,
          quantity,
          unit_price,
          unit_cost,
          line_total,
          valor_minimo_aplicado,
          valor_sem_minimo,
        };
      })
      .filter((it): it is NormalizedItem => Boolean(it));

    if (quantityRuleErrors.length > 0) {
      return NextResponse.json({ error: quantityRuleErrors[0] }, { status: 400 });
    }

    if (normalizedItems.length === 0) {
      return NextResponse.json({ error: "A IA não gerou itens válidos." }, { status: 502 });
    }

    const subtotal = normalizedItems.reduce((acc, it) => acc + (it.line_total || 0), 0);
    const discount = 0;
    const shipping = 0;
    const total = Math.max(0, subtotal - discount + shipping);

    const draftPayload = {
        client_id: clientId,
        client_whatsapp: draft.client_whatsapp || null,
        status: draft.status ?? "draft",
        title: draft.title ?? null,
        subtotal,
        discount,
        shipping,
        total,
        expires_at: draft.expires_at ?? null,
        delivery_date: draft.delivery_date ?? null,
        notes: draft.notes ?? null,
        items: normalizedItems.map((it, idx) => ({
        position: idx + 1,
        product_id: it.product_id,
        unit_type: it.unit_type,
        width_m: it.width_m,
        height_m: it.height_m,
        length_m: it.length_m,
        title: it.title,
        description: it.description,
        quantity: it.quantity,
        unit_price: it.unit_price,
        unit_cost: it.unit_cost,
        line_total: it.line_total,
        valor_minimo_aplicado: it.valor_minimo_aplicado,
        valor_sem_minimo: it.valor_sem_minimo,
      }))
    };

    return NextResponse.json({ draftPayload });
  } catch (err: unknown) {
    console.error("Erro em /api/ia/criar-orcamento:", err);

    const status =
      typeof (err as { status?: unknown })?.status === "number"
        ? ((err as { status?: number }).status ?? undefined)
        : undefined;
    const message = err instanceof Error ? err.message : "";

    if (message.includes("GEMINI_API_KEY")) {
      return NextResponse.json({ error: "GEMINI_API_KEY não configurada no backend." }, { status: 500 });
    }

    if (message.includes("OPENAI_API_KEY")) {
      return NextResponse.json({ error: "OPENAI_API_KEY não configurada no backend." }, { status: 500 });
    }

    if (status === 401 || status === 403) {
      return NextResponse.json({ error: "Chave do provedor de IA inválida ou sem permissão." }, { status: 502 });
    }

    if (status === 429 || message.toLowerCase().includes("resource_exhausted") || message.includes("429")) {
      return NextResponse.json({ error: "Limite do provedor de IA atingido. Tente novamente mais tarde." }, { status: 429 });
    }

    return NextResponse.json({ error: "Falha ao criar orçamento com IA." }, { status: 500 });
  }
}
