import { NextResponse } from "next/server";
import { createHash, randomUUID } from "node:crypto";
import { eq } from "drizzle-orm";
import { db } from "@/db";
import { orderAttempts } from "@/db/schema";
import { calculateDatabaseCart } from "@/lib/cart-server";
import { siteConfig } from "@/config/site";
import { createBotOrder, getBotOrder, quoteBotDelivery } from "@/lib/bot-api";
import { checkoutSchema } from "@/lib/validation";
import { ApiRequestError, checkRateLimit, readJson } from "@/lib/api-security";
import { isSettingEnabled } from "@/lib/auth";

function payloadHash(data: typeof checkoutSchema._type) {
  const normalized = { ...data, items: [...data.items].sort((a, b) => a.dishId.localeCompare(b.dishId)) };
  return createHash("sha256").update(JSON.stringify(normalized)).digest("hex");
}

/** The bot splits SBIS comments on `#`; the symbol is stripped from user text (contract §1). */
function cleanComment(value: string | undefined) {
  return value?.replace(/#/gu, "").trim().slice(0, 500) || undefined;
}

function responseFor(clientRef: string, bot: { order_id?: string; status?: string; pay_url?: string | null; track_url?: string; tg_link?: string | null; total?: number; delivery_fee?: number; moderation_id?: string | null }, status = 200) {
  return NextResponse.json({ orderId: bot.order_id || null, clientRef, status: bot.status || "pending_confirmation", paymentUrl: bot.pay_url || null, trackUrl: bot.track_url || "", tgLink: bot.tg_link || null, total: bot.total ?? null, deliveryFee: bot.delivery_fee ?? null }, { status, headers: { "Cache-Control": "no-store" } });
}

async function saveAttempt(clientRef: string, hash: string, patch: Partial<typeof orderAttempts.$inferInsert>) {
  if (!db) return;
  await db.update(orderAttempts).set({ ...patch, updatedAt: new Date() }).where(eq(orderAttempts.clientRef, clientRef));
}

export async function POST(request: Request) {
  try {
    checkRateLimit(request, "order", 5);
    if (!(await isSettingEnabled("orders_enabled"))) return NextResponse.json({ error: "orders_disabled", message: "Приём заказов временно отключён." }, { status: 503 });
    const parsed = checkoutSchema.safeParse(await readJson<unknown>(request, 64 * 1024));
    if (!parsed.success) return NextResponse.json({ error: "Invalid order" }, { status: 400 });
    if (!db) return NextResponse.json({ error: "service_unavailable", message: "Сервис заказов временно недоступен." }, { status: 503 });
    const data = parsed.data;
    const clientRef = data.clientRef || randomUUID();
    const hash = payloadHash(data);
    let [existing] = await db.select().from(orderAttempts).where(eq(orderAttempts.clientRef, clientRef)).limit(1);
    if (existing && existing.state === "rejected") {
      // Locally rejected attempts must not poison the reference: the client
      // fixes the cart/fields and retries with the same ref and a new payload.
      await db.delete(orderAttempts).where(eq(orderAttempts.clientRef, clientRef));
      existing = undefined!;
    }
    if (existing) {
      if (existing.payloadHash !== hash) return NextResponse.json({ error: "client_ref_conflict", message: "Эта заявка уже используется для другого заказа." }, { status: 409 });
      try { return responseFor(clientRef, await getBotOrder(clientRef), 200); } catch { return responseFor(clientRef, { order_id: existing.botOrderId || undefined, status: existing.botState || "pending_confirmation" }, 202); }
    }
    if (data.method === "pickup" && !(await isSettingEnabled("orders_pickup_enabled"))) return NextResponse.json({ error: "pickup_disabled", message: "Самовывоз временно недоступен. Оформите доставку или позвоните в ресторан." }, { status: 409 });
    const addressParts = [data.address, data.apartment ? `кв. ${data.apartment}` : "", data.floor ? `этаж ${data.floor}` : "", data.intercom ? `домофон ${data.intercom}` : ""].filter(Boolean);
    if (data.method === "delivery" && (!data.address || data.address.trim().length < 5)) return NextResponse.json({ error: "bad_request", message: "Укажите адрес доставки." }, { status: 400 });
    const due = data.desiredTime && !/^как можно скорее$/i.test(data.desiredTime.trim()) ? data.desiredTime : undefined;
    if (due && Number.isNaN(Date.parse(due))) return NextResponse.json({ error: "datetime_invalid", message: "Проверьте дату и время заказа." }, { status: 400 });
    if (due && new Date(due).getTime() < Date.now()) return NextResponse.json({ error: "datetime_past", message: "Выбранное время уже прошло." }, { status: 409 });
    if (due && !(await isSettingEnabled("orders_deferred_enabled"))) return NextResponse.json({ error: "deferred_disabled", message: "Отложенные заказы временно недоступны. Оформите заказ на ближайшее время." }, { status: 409 });
    const totals = await calculateDatabaseCart(data.items, data.method);
    if (!totals.lines.length || totals.invalid) {
      return NextResponse.json({ error: "Позиция недоступна или её цена изменилась", lines: totals.lines.map((line) => ({ dishId: line.requested.dishId, unavailable: line.unavailable, reason: line.reason })) }, { status: 409 });
    }
    if (totals.belowMinimum) {
      return NextResponse.json({ error: "min_sum", message: `Минимальная сумма заказа — ${siteConfig.order.minimumAmount} ₽. Добавьте позиций ещё на ${Math.ceil((siteConfig.order.minimumAmount - totals.subtotal) * 100) / 100} ₽.` }, { status: 409 });
    }
    if (data.method === "delivery") {
      // Fresh zone/minimum check via the bot (§8.2.4); a unavailable quote
      // service does not block the order — the bot re-validates on creation.
      try {
        await quoteBotDelivery(addressParts.join(", "), Math.round(totals.subtotal * 100) / 100);
      } catch (quoteError) {
        const quoteBody = (quoteError as { body?: { error?: string; message?: string } }).body;
        if (quoteBody?.error === "zone_unknown") return NextResponse.json({ error: "zone_unknown", message: quoteBody.message || "Адрес вне зон доставки. Проверьте адрес или выберите самовывоз." }, { status: 409 });
        if (quoteBody?.error === "min_sum") return NextResponse.json({ error: "min_sum", message: quoteBody.message || "Сумма заказа меньше минимальной для вашей зоны. Добавьте позиции или выберите самовывоз." }, { status: 409 });
      }
    }
    const inserted = await db.insert(orderAttempts).values({ clientRef, payloadHash: hash, state: "creating" }).onConflictDoNothing({ target: orderAttempts.clientRef }).returning({ id: orderAttempts.id });
    if (!inserted.length) {
      // A concurrent request with the same ref won the insert race: poll it.
      try { return responseFor(clientRef, await getBotOrder(clientRef), 200); } catch { return responseFor(clientRef, {}, 202); }
    }
    const botPayload = {
      client_ref: clientRef,
      delivery_type: data.method,
      address: data.method === "delivery" ? addressParts.join(", ") : undefined,
      due,
      customer: { name: data.name, phone: data.phone, email: data.email || undefined },
      comment: cleanComment(data.orderComment),
      courier_comment: data.method === "delivery" ? cleanComment(data.courierComment) : undefined,
      items: totals.lines.map((line) => ({ sbis_id: line.sbisId, qty: line.requested.quantity, price_snapshot: line.variant ? line.variant.priceKopecks / 100 : 0 }))
    };
    try {
      const bot = await createBotOrder(botPayload);
      await saveAttempt(clientRef, hash, { state: "submitted", botOrderId: bot.order_id, moderationId: bot.moderation_id || null, botState: bot.status, botResponse: { status: bot.status, total: bot.total, delivery_fee: bot.delivery_fee } });
      return responseFor(clientRef, bot);
    } catch {
      // The bot may have accepted the request before the network timed out.
      try {
        const bot = await getBotOrder(clientRef);
        await saveAttempt(clientRef, hash, { state: "submitted", botOrderId: bot.order_id, botState: bot.status });
        return responseFor(clientRef, bot);
      } catch {
        await saveAttempt(clientRef, hash, { state: "pending_confirmation", botState: "pending_confirmation" });
        return responseFor(clientRef, { status: "pending_confirmation" }, 202);
      }
    }
  } catch (error) {
    const status = error instanceof ApiRequestError ? error.status : (typeof (error as { status?: unknown })?.status === "number" ? Number((error as { status: number }).status) : 503);
    const body = (error as { body?: Record<string, unknown> })?.body;
    return NextResponse.json({ error: body?.error || (error instanceof Error ? error.message : "Unable to create order"), message: body?.message || (error instanceof Error ? error.message : "Не удалось создать заказ"), items_actual: body?.items_actual }, { status });
  }
}
