import { NextResponse } from "next/server";
import { revalidatePath } from "next/cache";
import { and, desc, eq, isNull } from "drizzle-orm";
import { db } from "@/db";
import { adminAuditEvents, menuCategories, menuItemSbisLinks, menuItems, menuItemVariants, sbisCatalogItems, sbisSyncState } from "@/db/schema";
import { adminAuthorized } from "@/lib/admin-auth";
import { recordAudit } from "@/lib/audit";

function fail(message: string, status = 400) { return NextResponse.json({ error: message }, { status }); }

export async function GET(request: Request) {
  if (!(await adminAuthorized(request))) return fail("Unauthorized", 401);
  if (!db) return fail("Database is not configured", 503);
  try {
    const [categories, items, variants, links, catalog, syncState, auditEvents] = await Promise.all([
      db.select().from(menuCategories).orderBy(menuCategories.sortOrder),
      db.select().from(menuItems).orderBy(menuItems.sortOrder),
      db.select().from(menuItemVariants).orderBy(menuItemVariants.sortOrder),
      db.select().from(menuItemSbisLinks),
      db.select({ sbisId: sbisCatalogItems.sbisId, categoryId: sbisCatalogItems.categoryId, categoryName: sbisCatalogItems.categoryName }).from(sbisCatalogItems),
      db.select().from(sbisSyncState).where(eq(sbisSyncState.id, 1)).limit(1),
      db.select().from(adminAuditEvents).orderBy(desc(adminAuditEvents.createdAt)).limit(15)
    ]);
    const activeLinks = links.filter((link) => !link.unlinkedAt);
    const catalogBySbis = new Map(catalog.map((row) => [row.sbisId, row]));
    const categorySbis: Record<string, { id: string | null; name: string }[]> = {};
    for (const item of items) {
      const link = activeLinks.find((candidate) => candidate.menuItemId === item.id);
      const sbis = link ? catalogBySbis.get(link.sbisId) : undefined;
      if (!sbis) continue;
      const list = categorySbis[item.categoryId] || (categorySbis[item.categoryId] = []);
      if (!list.some((entry) => entry.id === sbis.categoryId)) list.push({ id: sbis.categoryId, name: sbis.categoryName });
    }
    return NextResponse.json({ categories, items, variants, links, syncState: syncState[0] || null, auditEvents, categorySbis });
  } catch { return fail("Не удалось подключиться к базе меню. Проверьте DATABASE_URL и примените миграцию.", 503); }
}

async function resolveSbisCategoryBinding(sbisCategoryId: string | null) {
  if (!sbisCategoryId) return { sbisCategoryId: null, sbisCategoryNameSnapshot: null, categorySyncState: "unlinked" };
  const [row] = await db!.select({ categoryName: sbisCatalogItems.categoryName }).from(sbisCatalogItems).where(eq(sbisCatalogItems.categoryId, sbisCategoryId)).limit(1);
  return row
    ? { sbisCategoryId, sbisCategoryNameSnapshot: row.categoryName, categorySyncState: "matched" }
    : { sbisCategoryId, sbisCategoryNameSnapshot: null, categorySyncState: "missing_in_sbis" };
}

export async function POST(request: Request) {
  if (!(await adminAuthorized(request, true))) return fail("Unauthorized", 401);
  if (!db) return fail("Database is not configured", 503);
  const body = await request.json();
  try {
    if (body.type === "category") await db.insert(menuCategories).values({ slug: body.slug, name: body.name, yandexCategoryId: Number(body.yandexCategoryId), sortOrder: Number(body.sortOrder || 0) });
    else if (body.type === "item") {
      const [category] = await db.select().from(menuCategories).where(eq(menuCategories.slug, body.categorySlug));
      if (!category) return fail("Category not found");
      const [item] = await db.insert(menuItems).values({ slug: body.slug, categoryId: category.id, name: body.name, description: body.description || null, shortDescription: body.shortDescription || null, ingredients: body.ingredients || null, weightText: body.weightText || null, imageUrl: body.imageUrl || null, isAlcohol: Boolean(body.isAlcohol), showDetails: body.showDetails !== false, allowOnlineOrder: !body.isAlcohol && body.allowOnlineOrder !== false, allowDelivery: !body.isAlcohol && body.allowDelivery !== false, publishToYandex: Boolean(body.publishToYandex) && !body.isAlcohol, source: "manual" }).returning();
      if (body.priceRubles) await db.insert(menuItemVariants).values({ itemId: item.id, label: body.weightText || "1 порция", priceKopecks: Math.round(Number(body.priceRubles) * 100), yandexOfferId: body.yandexOfferId || `${body.slug}-main` });
    } else if (body.type === "variant") await db.insert(menuItemVariants).values({ itemId: Number(body.itemId), label: body.label, volumeMl: body.volumeMl ? Number(body.volumeMl) : null, weightGrams: body.weightGrams ? Number(body.weightGrams) : null, priceKopecks: Math.round(Number(body.priceRubles) * 100), yandexOfferId: body.yandexOfferId, sortOrder: Number(body.sortOrder || 0) });
    else return fail("Unknown operation");
    await recordAudit("menu_create", { entityType: String(body.type), entityId: body.slug || body.id || null, details: body.type === "item" ? { name: body.name } : undefined });
    revalidatePath("/menu"); return NextResponse.json({ ok: true });
  } catch (error) { return fail(error instanceof Error ? error.message : "Save failed"); }
}

export async function PATCH(request: Request) {
  if (!(await adminAuthorized(request, true))) return fail("Unauthorized", 401);
  if (!db) return fail("Database is not configured", 503);
  const body = await request.json();
  try {
    if (body.type === "category") {
      const patch: Record<string, unknown> = { name: body.name, slug: body.slug, yandexCategoryId: Number(body.yandexCategoryId), sortOrder: Number(body.sortOrder || 0), isActive: body.isActive !== false, archivedAt: body.archivedAt === null ? null : undefined, updatedAt: new Date() };
      if (body.sbisCategoryId === null || typeof body.sbisCategoryId === "string") {
        Object.assign(patch, await resolveSbisCategoryBinding(body.sbisCategoryId && body.sbisCategoryId.trim() ? body.sbisCategoryId.trim() : null));
      }
      await db.update(menuCategories).set(patch).where(eq(menuCategories.id, Number(body.id)));
    }
    else if (body.type === "item") {
      const isAlcohol = Boolean(body.isAlcohol);
      const [current] = await db.select({ showDetails: menuItems.showDetails }).from(menuItems).where(eq(menuItems.id, Number(body.id))).limit(1);
      await db.update(menuItems).set({ name: body.name, description: body.description || null, shortDescription: body.shortDescription || null, ingredients: body.ingredients || null, weightText: body.weightText || null, imageUrl: body.imageUrl || null, isAvailable: body.isAvailable !== false, showOnWebsite: body.showOnWebsite !== false, archivedAt: body.archivedAt === null ? null : undefined, showDetails: body.showDetails === undefined ? current?.showDetails ?? true : body.showDetails !== false, isAlcohol, allowOnlineOrder: !isAlcohol && body.allowOnlineOrder !== false, allowDelivery: !isAlcohol && body.allowDelivery !== false, publishToYandex: !isAlcohol && body.publishToYandex === true, sortOrder: Number(body.sortOrder || 0), updatedAt: new Date() }).where(eq(menuItems.id, Number(body.id)));
      if (body.priceRubles !== undefined && body.priceRubles !== "") {
        const [variant] = await db.select().from(menuItemVariants).where(eq(menuItemVariants.itemId, Number(body.id))).limit(1);
        if (variant) await db.update(menuItemVariants).set({ priceKopecks: Math.round(Number(body.priceRubles) * 100), updatedAt: new Date() }).where(eq(menuItemVariants.id, variant.id));
        else await db.insert(menuItemVariants).values({ itemId: Number(body.id), label: body.weightText || "1 порция", priceKopecks: Math.round(Number(body.priceRubles) * 100), yandexOfferId: `${body.slug || `item-${body.id}`}-main` });
      }
    } else if (body.type === "variant") await db.update(menuItemVariants).set({ label: body.label, volumeMl: body.volumeMl ? Number(body.volumeMl) : null, weightGrams: body.weightGrams ? Number(body.weightGrams) : null, priceKopecks: Math.round(Number(body.priceRubles) * 100), isAvailable: body.isAvailable !== false, sortOrder: Number(body.sortOrder || 0), updatedAt: new Date() }).where(eq(menuItemVariants.id, Number(body.id)));
    else return fail("Unknown operation");
    await recordAudit("menu_update", { entityType: String(body.type), entityId: String(body.id ?? ""), details: body.sbisCategoryId !== undefined ? { sbis_category_id: body.sbisCategoryId } : undefined });
    revalidatePath("/menu"); return NextResponse.json({ ok: true });
  } catch (error) { return fail(error instanceof Error ? error.message : "Update failed"); }
}

export async function DELETE(request: Request) {
  if (!(await adminAuthorized(request, true))) return fail("Unauthorized", 401);
  if (!db) return fail("Database is not configured", 503);
  const body = await request.json();
  try {
    if (body.type === "category") {
      const [item] = await db.select({ id: menuItems.id }).from(menuItems).where(and(eq(menuItems.categoryId, Number(body.id)), eq(menuItems.showOnWebsite, true))).limit(1);
      if (item) return fail("Перенесите позиции из категории перед архивированием");
      await db.update(menuCategories).set({ isActive: false, archivedAt: new Date(), updatedAt: new Date() }).where(eq(menuCategories.id, Number(body.id)));
    } else if (body.type === "item") {
      // §4.3: first deletion transactionally removes the SBIS link, disables
      // publication/ordering and archives the card.
      const archivedAt = new Date();
      await db.transaction(async (tx) => {
        await tx.update(menuItems).set({ showOnWebsite: false, isAvailable: false, archivedAt, updatedAt: archivedAt }).where(eq(menuItems.id, Number(body.id)));
        await tx.update(menuItemSbisLinks).set({ unlinkedAt: archivedAt, unlinkReason: "archived" }).where(and(eq(menuItemSbisLinks.menuItemId, Number(body.id)), isNull(menuItemSbisLinks.unlinkedAt)));
      });
    }
    else return fail("Unknown operation");
    await recordAudit("menu_archive", { entityType: String(body.type), entityId: String(body.id ?? "") });
    revalidatePath("/menu"); return NextResponse.json({ ok: true });
  } catch (error) { return fail(error instanceof Error ? error.message : "Delete failed"); }
}
