import {
  CreateManagementLevelGroupSchema,
  CreateManagementLevelSchema,
  UpdateManagementLevelGroupSchema,
  UpdateManagementLevelSchema,
} from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { createAuditEntry } from "../lib/audit.js";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc.js";

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

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

  getGroupById: protectedProcedure
    .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 }) => {
      const existing = await ctx.db.managementLevelGroup.findUnique({ where: { name: input.name } });
      if (existing) {
        throw new TRPCError({ code: "CONFLICT", message: `Group "${input.name}" already exists` });
      }
      const created = await ctx.db.managementLevelGroup.create({
        data: {
          name: input.name,
          targetPercentage: input.targetPercentage,
          sortOrder: input.sortOrder,
        },
        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) {
        const conflict = await ctx.db.managementLevelGroup.findUnique({ where: { name: input.data.name } });
        if (conflict) {
          throw new TRPCError({ code: "CONFLICT", message: `Group "${input.data.name}" already exists` });
        }
      }

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

      const updated = await ctx.db.managementLevelGroup.update({
        where: { id: input.id },
        data: {
          ...(input.data.name !== undefined ? { name: input.data.name } : {}),
          ...(input.data.targetPercentage !== undefined ? { targetPercentage: input.data.targetPercentage } : {}),
          ...(input.data.sortOrder !== undefined ? { sortOrder: input.data.sortOrder } : {}),
        },
        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",
      );

      const existing = await ctx.db.managementLevel.findUnique({ where: { name: input.name } });
      if (existing) {
        throw new TRPCError({ code: "CONFLICT", message: `Level "${input.name}" already exists` });
      }

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

      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) {
        const conflict = await ctx.db.managementLevel.findUnique({ where: { name: input.data.name } });
        if (conflict) {
          throw new TRPCError({ code: "CONFLICT", message: `Level "${input.data.name}" already exists` });
        }
      }

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

      const updated = await ctx.db.managementLevel.update({
        where: { id: input.id },
        data: {
          ...(input.data.name !== undefined ? { name: input.data.name } : {}),
          ...(input.data.groupId !== undefined ? { groupId: input.data.groupId } : {}),
        },
      });

      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",
      );
      if (level._count.resources > 0) {
        throw new TRPCError({
          code: "PRECONDITION_FAILED",
          message: `Cannot delete level assigned to ${level._count.resources} resource(s)`,
        });
      }
      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 };
    }),
});
