import { CreateOrgUnitSchema, UpdateOrgUnitSchema } from "@capakraken/shared";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { createAuditEntry } from "../lib/audit.js";
import {
  adminProcedure,
  createTRPCRouter,
  protectedProcedure,
  resourceOverviewProcedure,
} from "../trpc.js";
import {
  assertOrgUnitParentLevel,
  buildOrgUnitCreateData,
  buildOrgUnitListWhere,
  buildOrgUnitTree,
  buildOrgUnitUpdateData,
  findOrgUnitByIdentifier,
} from "./org-unit-support.js";

type OrgUnitIdentifierReadModel = {
  id: string;
  name: string;
  shortName: string | null;
  level: number;
  isActive: boolean;
};

type OrgUnitDetailReadModel = OrgUnitIdentifierReadModel & {
  parentId: string | null;
  sortOrder: number;
  createdAt: Date;
  updatedAt: Date;
  _count: { resources: number };
};

export const orgUnitRouter = createTRPCRouter({
  list: resourceOverviewProcedure
    .input(
      z.object({
        level: z.number().int().min(5).max(7).optional(),
        parentId: z.string().optional(),
        isActive: z.boolean().optional(),
      }).optional(),
    )
    .query(async ({ ctx, input }) => {
      return ctx.db.orgUnit.findMany({
        where: buildOrgUnitListWhere(input ?? {}),
        orderBy: [{ level: "asc" }, { sortOrder: "asc" }, { name: "asc" }],
      });
    }),

  getTree: resourceOverviewProcedure
    .input(z.object({ isActive: z.boolean().optional() }).optional())
    .query(async ({ ctx, input }) => {
      const all = await ctx.db.orgUnit.findMany({
        where: buildOrgUnitListWhere({ isActive: input?.isActive }),
        orderBy: [{ sortOrder: "asc" }, { name: "asc" }],
      });
      return buildOrgUnitTree(all);
    }),

  getById: resourceOverviewProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const unit = await findUniqueOrThrow(
        ctx.db.orgUnit.findUnique({
          where: { id: input.id },
          include: {
            parent: true,
            children: { orderBy: { sortOrder: "asc" } },
            _count: { select: { resources: true } },
          },
        }),
        "Org unit",
      );
      return unit;
    }),

  resolveByIdentifier: protectedProcedure
    .input(z.object({ identifier: z.string().trim().min(1) }))
    .query(async ({ ctx, input }) => {
      return findOrgUnitByIdentifier<OrgUnitIdentifierReadModel>(ctx.db, input.identifier, {
        select: {
          id: true,
          name: true,
          shortName: true,
          level: true,
          isActive: true,
        },
      });
    }),

  getByIdentifier: resourceOverviewProcedure
    .input(z.object({ identifier: z.string().trim().min(1) }))
    .query(async ({ ctx, input }) => {
      return findOrgUnitByIdentifier<OrgUnitDetailReadModel>(ctx.db, input.identifier, {
        include: { _count: { select: { resources: true } } },
      });
    }),

  create: adminProcedure
    .input(CreateOrgUnitSchema)
    .mutation(async ({ ctx, input }) => {
      if (input.parentId) {
        const parent = await findUniqueOrThrow(
          ctx.db.orgUnit.findUnique({ where: { id: input.parentId } }),
          "Parent org unit",
        );
        assertOrgUnitParentLevel(parent, input.level);
      }

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

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

      return created;
    }),

  update: adminProcedure
    .input(z.object({ id: z.string(), data: UpdateOrgUnitSchema }))
    .mutation(async ({ ctx, input }) => {
      const existing = await findUniqueOrThrow(
        ctx.db.orgUnit.findUnique({ where: { id: input.id } }),
        "Org unit",
      );

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

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

      void createAuditEntry({
        db: ctx.db,
        entityType: "OrgUnit",
        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;
    }),

  deactivate: adminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const updated = await ctx.db.orgUnit.update({
        where: { id: input.id },
        data: { isActive: false },
      });

      void createAuditEntry({
        db: ctx.db,
        entityType: "OrgUnit",
        entityId: updated.id,
        entityName: updated.name,
        action: "UPDATE",
        userId: ctx.dbUser?.id,
        before: { isActive: true },
        after: { isActive: false },
        source: "ui",
        summary: "Deactivated OrgUnit",
      });

      return updated;
    }),
});
