import { BlueprintTarget, CreateResourceSchema, PermissionKey, ResourceRoleSchema, UpdateResourceSchema, inferStateFromPostalCode } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { ROLE_BRIEF_SELECT } from "../db/selects.js";
import { managerProcedure, requirePermission } from "../trpc.js";
import { assertBlueprintDynamicFields } from "./blueprint-validation.js";

export const resourceMutationProcedures = {
  create: managerProcedure
    .input(CreateResourceSchema.extend({ roles: z.array(ResourceRoleSchema).optional() }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_RESOURCES);
      const existing = await ctx.db.resource.findFirst({
        where: { OR: [{ eid: input.eid }, { email: input.email }] },
      });

      if (existing) {
        throw new TRPCError({
          code: "CONFLICT",
          message: `Resource with EID "${input.eid}" or email "${input.email}" already exists`,
        });
      }

      await assertBlueprintDynamicFields({
        db: ctx.db,
        blueprintId: input.blueprintId,
        dynamicFields: input.dynamicFields,
        target: BlueprintTarget.RESOURCE,
      });

      const primaryCount = (input.roles ?? []).filter((role) => role.isPrimary).length;
      if (primaryCount > 1) {
        throw new TRPCError({ code: "BAD_REQUEST", message: "A resource can have at most one primary role" });
      }

      const resource = await ctx.db.resource.create({
        data: {
          eid: input.eid,
          displayName: input.displayName,
          email: input.email,
          chapter: input.chapter,
          lcrCents: input.lcrCents,
          ucrCents: input.ucrCents,
          currency: input.currency,
          chargeabilityTarget: input.chargeabilityTarget,
          availability: input.availability,
          skills: input.skills as unknown as import("@capakraken/db").Prisma.InputJsonValue,
          dynamicFields: input.dynamicFields as unknown as import("@capakraken/db").Prisma.InputJsonValue,
          blueprintId: input.blueprintId,
          portfolioUrl: input.portfolioUrl || undefined,
          roleId: input.roleId || undefined,
          ...(input.postalCode !== undefined ? { postalCode: input.postalCode } : {}),
          ...(input.postalCode && !input.federalState
            ? { federalState: inferStateFromPostalCode(input.postalCode) }
            : input.federalState !== undefined
              ? { federalState: input.federalState }
              : {}),
          ...(input.countryId !== undefined ? { countryId: input.countryId || null } : {}),
          ...(input.metroCityId !== undefined ? { metroCityId: input.metroCityId || null } : {}),
          ...(input.orgUnitId !== undefined ? { orgUnitId: input.orgUnitId || null } : {}),
          ...(input.managementLevelGroupId !== undefined ? { managementLevelGroupId: input.managementLevelGroupId || null } : {}),
          ...(input.managementLevelId !== undefined ? { managementLevelId: input.managementLevelId || null } : {}),
          ...(input.resourceType !== undefined ? { resourceType: input.resourceType } : {}),
          ...(input.chgResponsibility !== undefined ? { chgResponsibility: input.chgResponsibility } : {}),
          ...(input.rolledOff !== undefined ? { rolledOff: input.rolledOff } : {}),
          ...(input.departed !== undefined ? { departed: input.departed } : {}),
          ...(input.enterpriseId !== undefined ? { enterpriseId: input.enterpriseId || null } : {}),
          ...(input.clientUnitId !== undefined ? { clientUnitId: input.clientUnitId || null } : {}),
          ...(input.fte !== undefined ? { fte: input.fte } : {}),
          resourceRoles: input.roles?.length
            ? {
                create: input.roles.map((role) => ({
                  roleId: role.roleId,
                  isPrimary: role.isPrimary,
                })),
              }
            : undefined,
        } as unknown as Parameters<typeof ctx.db.resource.create>[0]["data"],
        include: {
          resourceRoles: { include: { role: { select: ROLE_BRIEF_SELECT } } },
        },
      });

      await ctx.db.auditLog.create({
        data: {
          entityType: "Resource",
          entityId: resource.id,
          action: "CREATE",
          userId: ctx.dbUser?.id,
          changes: { after: resource },
        } as unknown as Parameters<typeof ctx.db.auditLog.create>[0]["data"],
      });

      return resource;
    }),

  update: managerProcedure
    .input(z.object({ id: z.string(), data: UpdateResourceSchema.extend({ roles: z.array(ResourceRoleSchema).optional() }) }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_RESOURCES);
      const existing = await findUniqueOrThrow(
        ctx.db.resource.findUnique({ where: { id: input.id } }),
        "Resource",
      );

      const nextBlueprintId = input.data.blueprintId ?? existing.blueprintId ?? undefined;
      const nextDynamicFields = (input.data.dynamicFields ?? existing.dynamicFields ?? {}) as Record<string, unknown>;

      await assertBlueprintDynamicFields({
        db: ctx.db,
        blueprintId: nextBlueprintId,
        dynamicFields: nextDynamicFields,
        target: BlueprintTarget.RESOURCE,
      });

      if (input.data.roles !== undefined) {
        const primaryCount = input.data.roles.filter((role) => role.isPrimary).length;
        if (primaryCount > 1) {
          throw new TRPCError({ code: "BAD_REQUEST", message: "A resource can have at most one primary role" });
        }
      }

      const updated = await ctx.db.resource.update({
        where: { id: input.id },
        data: {
          ...(input.data.displayName !== undefined ? { displayName: input.data.displayName } : {}),
          ...(input.data.email !== undefined ? { email: input.data.email } : {}),
          ...(input.data.chapter !== undefined ? { chapter: input.data.chapter } : {}),
          ...(input.data.lcrCents !== undefined ? { lcrCents: input.data.lcrCents } : {}),
          ...(input.data.ucrCents !== undefined ? { ucrCents: input.data.ucrCents } : {}),
          ...(input.data.currency !== undefined ? { currency: input.data.currency } : {}),
          ...(input.data.chargeabilityTarget !== undefined ? { chargeabilityTarget: input.data.chargeabilityTarget } : {}),
          ...(input.data.availability !== undefined ? { availability: input.data.availability as unknown as import("@capakraken/db").Prisma.InputJsonValue } : {}),
          ...(input.data.skills !== undefined ? { skills: input.data.skills as unknown as import("@capakraken/db").Prisma.InputJsonValue } : {}),
          ...(input.data.dynamicFields !== undefined ? { dynamicFields: input.data.dynamicFields as unknown as import("@capakraken/db").Prisma.InputJsonValue } : {}),
          ...(input.data.blueprintId !== undefined ? { blueprintId: input.data.blueprintId } : {}),
          ...(input.data.isActive !== undefined ? { isActive: input.data.isActive } : {}),
          ...(input.data.portfolioUrl !== undefined ? { portfolioUrl: input.data.portfolioUrl || null } : {}),
          ...(input.data.roleId !== undefined ? { roleId: input.data.roleId || null } : {}),
          ...(input.data.postalCode !== undefined ? { postalCode: input.data.postalCode } : {}),
          ...(input.data.postalCode && !input.data.federalState
            ? { federalState: inferStateFromPostalCode(input.data.postalCode) }
            : input.data.federalState !== undefined
              ? { federalState: input.data.federalState }
              : {}),
          ...(input.data.countryId !== undefined ? { countryId: input.data.countryId || null } : {}),
          ...(input.data.metroCityId !== undefined ? { metroCityId: input.data.metroCityId || null } : {}),
          ...(input.data.orgUnitId !== undefined ? { orgUnitId: input.data.orgUnitId || null } : {}),
          ...(input.data.managementLevelGroupId !== undefined ? { managementLevelGroupId: input.data.managementLevelGroupId || null } : {}),
          ...(input.data.managementLevelId !== undefined ? { managementLevelId: input.data.managementLevelId || null } : {}),
          ...(input.data.resourceType !== undefined ? { resourceType: input.data.resourceType } : {}),
          ...(input.data.chgResponsibility !== undefined ? { chgResponsibility: input.data.chgResponsibility } : {}),
          ...(input.data.rolledOff !== undefined ? { rolledOff: input.data.rolledOff } : {}),
          ...(input.data.departed !== undefined ? { departed: input.data.departed } : {}),
          ...(input.data.enterpriseId !== undefined ? { enterpriseId: input.data.enterpriseId || null } : {}),
          ...(input.data.clientUnitId !== undefined ? { clientUnitId: input.data.clientUnitId || null } : {}),
          ...(input.data.fte !== undefined ? { fte: input.data.fte } : {}),
        } as unknown as Parameters<typeof ctx.db.resource.update>[0]["data"],
        include: {
          resourceRoles: { include: { role: { select: ROLE_BRIEF_SELECT } } },
        },
      });

      if (input.data.roles !== undefined) {
        await ctx.db.resourceRole.deleteMany({ where: { resourceId: input.id } });
        if (input.data.roles.length > 0) {
          await ctx.db.resourceRole.createMany({
            data: input.data.roles.map((role) => ({
              resourceId: input.id,
              roleId: role.roleId,
              isPrimary: role.isPrimary,
            })),
          });
        }
      }

      await ctx.db.auditLog.create({
        data: {
          entityType: "Resource",
          entityId: input.id,
          action: "UPDATE",
          changes: { before: existing, after: updated },
        },
      });

      return updated;
    }),

  deactivate: managerProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_RESOURCES);
      const resource = await ctx.db.resource.update({
        where: { id: input.id },
        data: { isActive: false },
      });

      await ctx.db.auditLog.create({
        data: {
          entityType: "Resource",
          entityId: input.id,
          action: "UPDATE",
          changes: { after: { isActive: false } },
        },
      });

      return resource;
    }),

  batchDeactivate: managerProcedure
    .input(z.object({ ids: z.array(z.string()).min(1).max(100) }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_RESOURCES);
      const updated = await ctx.db.$transaction(
        input.ids.map((id) =>
          ctx.db.resource.update({ where: { id }, data: { isActive: false } }),
        ),
      );

      await ctx.db.auditLog.create({
        data: {
          entityType: "Resource",
          entityId: input.ids.join(","),
          action: "UPDATE",
          changes: { after: { isActive: false, ids: input.ids } },
        },
      });

      return { count: updated.length };
    }),

  batchUpdateCustomFields: managerProcedure
    .input(z.object({
      ids: z.array(z.string()).min(1).max(100),
      fields: z.record(z.string(), z.unknown()),
    }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_RESOURCES);

      await ctx.db.$transaction(
        input.ids.map((id) =>
          ctx.db.$executeRaw`
            UPDATE "Resource"
            SET "dynamicFields" = "dynamicFields" || ${JSON.stringify(input.fields)}::jsonb
            WHERE id = ${id}
          `,
        ),
      );

      await ctx.db.auditLog.create({
        data: {
          entityType: "Resource",
          entityId: input.ids.join(","),
          action: "UPDATE",
          changes: { after: { dynamicFields: input.fields, ids: input.ids } } as unknown as import("@capakraken/db").Prisma.InputJsonValue,
        },
      });

      return { updated: input.ids.length };
    }),
};
