import {
  CreateManagementLevelGroupSchema,
  CreateManagementLevelSchema,
  UpdateManagementLevelGroupSchema,
  UpdateManagementLevelSchema,
} from "@capakraken/shared";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { createAuditEntry } from "../lib/audit.js";
import { adminProcedure, createTRPCRouter, planningReadProcedure } from "../trpc.js";
import {
  assertManagementLevelDeletable,
  assertManagementLevelGroupNameAvailable,
  assertManagementLevelNameAvailable,
  buildManagementLevelCreateData,
  buildManagementLevelGroupCreateData,
  buildManagementLevelGroupUpdateData,
  buildManagementLevelUpdateData,
} from "./management-level-support.js";

export const managementLevelRouter = createTRPCRouter({
  // ─── Groups ─────────────────────────────────────────────

  listGroups: planningReadProcedure.query(async ({ ctx }) => {
    return ctx.db.managementLevelGroup.findMany({
      include: { levels: { orderBy: { name: "asc" } } },
      orderBy: { sortOrder: "asc" },
    });
  }),

  getGroupById: planningReadProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const group = await findUniqueOrThrow(
        ctx.db.managementLevelGroup.findUnique({
          where: { id: input.id },
          include: {
            levels: { orderBy: { name: "asc" } },
            _count: { select: { resources: true } },
          },
        }),
        "Management level group",
      );
      return group;
    }),

  createGroup: adminProcedure
    .input(CreateManagementLevelGroupSchema)
    .mutation(async ({ ctx, input }) => {
      await assertManagementLevelGroupNameAvailable(ctx.db, input.name);
      const created = await ctx.db.managementLevelGroup.create({
        data: buildManagementLevelGroupCreateData(input),
        include: { levels: true },
      });

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

      return created;
    }),

  updateGroup: adminProcedure
    .input(z.object({ id: z.string(), data: UpdateManagementLevelGroupSchema }))
    .mutation(async ({ ctx, input }) => {
      const existing = await findUniqueOrThrow(
        ctx.db.managementLevelGroup.findUnique({ where: { id: input.id } }),
        "Group",
      );

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

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

      const updated = await ctx.db.managementLevelGroup.update({
        where: { id: input.id },
        data: buildManagementLevelGroupUpdateData(input.data),
        include: { levels: true },
      });

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

      return updated;
    }),

  // ─── Levels ─────────────────────────────────────────────

  createLevel: adminProcedure
    .input(CreateManagementLevelSchema)
    .mutation(async ({ ctx, input }) => {
      await findUniqueOrThrow(
        ctx.db.managementLevelGroup.findUnique({ where: { id: input.groupId } }),
        "Group",
      );

      await assertManagementLevelNameAvailable(ctx.db, input.name);

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

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

      return created;
    }),

  updateLevel: adminProcedure
    .input(z.object({ id: z.string(), data: UpdateManagementLevelSchema }))
    .mutation(async ({ ctx, input }) => {
      const existing = await findUniqueOrThrow(
        ctx.db.managementLevel.findUnique({ where: { id: input.id } }),
        "Level",
      );

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

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

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

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

      return updated;
    }),

  deleteLevel: adminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const level = await findUniqueOrThrow(
        ctx.db.managementLevel.findUnique({
          where: { id: input.id },
          include: { _count: { select: { resources: true } } },
        }),
        "Level",
      );
      assertManagementLevelDeletable(level);
      await ctx.db.managementLevel.delete({ where: { id: input.id } });

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

      return { success: true };
    }),
});
