import type { PrismaClient } from "@prisma/client";
import { HttpError } from "./errors";
import { ok, created } from "./response";
import { parseIdParam } from "./schemas";
import { setCacheHeaders } from "./cacheHeaders";
import type { NextRequest } from "next/server";
import { requireAdmin } from "@/server/auth/requireAdmin";

interface CRUDOptions<TModel> {
  prisma: PrismaClient;
  model: keyof PrismaClient;
  requireAuth?: boolean;
  cacheDuration?: "short" | "medium" | "long" | "none";
  select?: Record<string, boolean>;
  orderBy?: Record<string, "asc" | "desc">;
}

/**
 * Generic GET handler for fetching all items.
 */
export async function handleGetAll<TModel>(
  request: Request,
  options: CRUDOptions<TModel>,
) {
  const { prisma, model, cacheDuration = "medium", select, orderBy } = options;
  
  const modelClient = prisma[model] as any;
  
  const findManyOptions: any = {};
  if (select) {
    findManyOptions.select = select;
  }
  if (orderBy) {
    findManyOptions.orderBy = orderBy;
  }

  const items = await modelClient.findMany(findManyOptions);
  
  const response = ok(items);
  return setCacheHeaders(response, cacheDuration);
}

/**
 * Generic GET handler for fetching a single item by ID.
 */
export async function handleGetById<TModel>(
  request: Request,
  params: Promise<{ id: string }>,
  options: CRUDOptions<TModel>,
) {
  const { prisma, model, cacheDuration = "medium", select } = options;
  const id = await parseIdParam(params);
  
  const modelClient = prisma[model] as any;
  
  const findUniqueOptions: any = { where: { id } };
  if (select) {
    findUniqueOptions.select = select;
  }

  const item = await modelClient.findUnique(findUniqueOptions);
  
  if (!item) {
    throw new HttpError(`${String(model)} not found`, { status: 404 });
  }

  const response = ok(item);
  return setCacheHeaders(response, cacheDuration);
}

/**
 * Generic POST handler for creating an item.
 */
export async function handleCreate<TModel>(
  request: Request,
  options: CRUDOptions<TModel>,
  parseData: (request: Request) => Promise<Record<string, unknown>>,
) {
  const { prisma, model, requireAuth = true } = options;
  
  if (requireAuth) {
    await requireAdmin(request);
  }

  const data = await parseData(request);
  const modelClient = prisma[model] as any;
  
  const newItem = await modelClient.create({ data });
  return created(newItem);
}

/**
 * Generic PUT handler for updating an item by ID.
 */
export async function handleUpdate<TModel>(
  request: Request,
  params: Promise<{ id: string }>,
  options: CRUDOptions<TModel>,
  parseData: (request: Request) => Promise<Record<string, unknown>>,
) {
  const { prisma, model, requireAuth = true } = options;
  
  if (requireAuth) {
    await requireAdmin(request);
  }

  const id = await parseIdParam(params);
  const data = await parseData(request);
  
  const modelClient = prisma[model] as any;
  
  // Check if item exists
  const existing = await modelClient.findUnique({ where: { id } });
  if (!existing) {
    throw new HttpError(`${String(model)} not found`, { status: 404 });
  }

  const updatedItem = await modelClient.update({
    where: { id },
    data,
  });

  return ok(updatedItem);
}

/**
 * Generic DELETE handler for deleting an item by ID.
 */
export async function handleDelete<TModel>(
  request: Request,
  params: Promise<{ id: string }>,
  options: CRUDOptions<TModel>,
) {
  const { prisma, model, requireAuth = true } = options;
  
  if (requireAuth) {
    await requireAdmin(request);
  }

  const id = await parseIdParam(params);
  const modelClient = prisma[model] as any;
  
  // Check if item exists
  const existing = await modelClient.findUnique({ where: { id } });
  if (!existing) {
    throw new HttpError(`${String(model)} not found`, { status: 404 });
  }

  await modelClient.delete({ where: { id } });
  
  return ok({ success: true });
}

/**
 * Generic handler for upsert operations (create or update).
 */
export async function handleUpsert<TModel>(
  request: Request,
  options: CRUDOptions<TModel>,
  parseData: (request: Request) => Promise<{ id?: number; [key: string]: unknown }>,
  whereClause?: (data: { id?: number; [key: string]: unknown }) => Record<string, unknown>,
) {
  const { prisma, model, requireAuth = true } = options;
  
  if (requireAuth) {
    await requireAdmin(request);
  }

  const data = await parseData(request);
  const modelClient = prisma[model] as any;
  
  let updatedItem;
  
  if (data.id) {
    // Update existing
    const id = data.id;
    delete data.id;
    
    const existing = await modelClient.findUnique({ where: { id } });
    if (!existing) {
      throw new HttpError(`${String(model)} not found`, { status: 404 });
    }

    updatedItem = await modelClient.update({
      where: { id },
      data,
    });
  } else {
    // Create new
    updatedItem = await modelClient.create({ data });
  }

  return ok(updatedItem);
}

