import {
  CreateUtilizationCategorySchema,
  UpdateUtilizationCategorySchema,
} from "@capakraken/shared";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { createAuditEntry } from "../lib/audit.js";
import type { TRPCContext } from "../trpc.js";
import {
  assertUtilizationCategoryCodeAvailable,
  buildUtilizationCategoryCreateData,
  buildUtilizationCategoryListWhere,
  buildUtilizationCategoryUpdateData,
  unsetDefaultUtilizationCategory,
} from "./utilization-category-support.js";

export const UtilizationCategoryListInputSchema = z.object({
  isActive: z.boolean().optional(),
}).optional();

export const UtilizationCategoryByIdInputSchema = z.object({
  id: z.string(),
});

export const CreateUtilizationCategoryInputSchema = CreateUtilizationCategorySchema;

export const UpdateUtilizationCategoryInputSchema = z.object({
  id: z.string(),
  data: UpdateUtilizationCategorySchema,
});

type UtilizationCategoryContext = Pick<TRPCContext, "db" | "dbUser">;

export async function listUtilizationCategories(
  ctx: Pick<TRPCContext, "db">,
  input: z.infer<typeof UtilizationCategoryListInputSchema>,
) {
  return ctx.db.utilizationCategory.findMany({
    where: buildUtilizationCategoryListWhere(input ?? {}),
    orderBy: { sortOrder: "asc" },
  });
}

export async function getUtilizationCategoryById(
  ctx: Pick<TRPCContext, "db">,
  input: z.infer<typeof UtilizationCategoryByIdInputSchema>,
) {
  return findUniqueOrThrow(
    ctx.db.utilizationCategory.findUnique({
      where: { id: input.id },
      include: { _count: { select: { projects: true } } },
    }),
    "Utilization category",
  );
}

export async function createUtilizationCategory(
  ctx: UtilizationCategoryContext,
  input: z.infer<typeof CreateUtilizationCategoryInputSchema>,
) {
  await assertUtilizationCategoryCodeAvailable(ctx.db, input.code);

  if (input.isDefault) {
    await unsetDefaultUtilizationCategory(ctx.db);
  }

  const created = await ctx.db.utilizationCategory.create({
    data: buildUtilizationCategoryCreateData(input),
  });

  void createAuditEntry({
    db: ctx.db,
    entityType: "UtilizationCategory",
    entityId: created.id,
    entityName: created.name,
    action: "CREATE",
    ...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
    after: created as unknown as Record<string, unknown>,
    source: "ui",
  });

  return created;
}

export async function updateUtilizationCategory(
  ctx: UtilizationCategoryContext,
  input: z.infer<typeof UpdateUtilizationCategoryInputSchema>,
) {
  const existing = await findUniqueOrThrow(
    ctx.db.utilizationCategory.findUnique({ where: { id: input.id } }),
    "Utilization category",
  );

  if (input.data.code && input.data.code !== existing.code) {
    await assertUtilizationCategoryCodeAvailable(ctx.db, input.data.code, existing.id);
  }

  if (input.data.isDefault) {
    await unsetDefaultUtilizationCategory(ctx.db, input.id);
  }

  const before = existing as unknown as Record<string, unknown>;

  const updated = await ctx.db.utilizationCategory.update({
    where: { id: input.id },
    data: buildUtilizationCategoryUpdateData(input.data),
  });

  void createAuditEntry({
    db: ctx.db,
    entityType: "UtilizationCategory",
    entityId: updated.id,
    entityName: updated.name,
    action: "UPDATE",
    ...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
    before,
    after: updated as unknown as Record<string, unknown>,
    source: "ui",
  });

  return updated;
}
