import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { ROLE_BRIEF_SELECT } from "../db/selects.js";
import {
  anonymizeResource,
  getAnonymizationDirectory,
} from "../lib/anonymization.js";
import { assertCanReadResource } from "../lib/resource-access.js";
import { protectedProcedure } from "../trpc.js";
import {
  readResourceByIdentifierDetailSnapshot,
  resolveResourceIdentifierSnapshot,
} from "./resource-read-shared.js";
import { mapResourceDetail } from "./resource-read-models.js";

export const resourceIdentifierReadProcedures = {
  resolveByIdentifier: protectedProcedure
    .input(z.object({ identifier: z.string() }))
    .query(async ({ ctx, input }) => {
      const resource = await resolveResourceIdentifierSnapshot(
        ctx,
        input.identifier,
        "You can only resolve your own resource unless you have staff access",
      );
      if ("error" in resource) {
        throw new TRPCError({ code: "NOT_FOUND", message: "Resource not found" });
      }
      return resource;
    }),

  getHoverCard: protectedProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const resource = await findUniqueOrThrow(
        ctx.db.resource.findUnique({
          where: { id: input.id },
          select: {
            id: true,
            displayName: true,
            eid: true,
            email: true,
            chapter: true,
            lcrCents: true,
            ucrCents: true,
            currency: true,
            chargeabilityTarget: true,
            skills: true,
            availability: true,
            isActive: true,
            areaRole: { select: ROLE_BRIEF_SELECT },
            country: { select: { name: true, code: true } },
            managementLevel: { select: { name: true } },
            resourceType: true,
          },
        }),
        "Resource",
      );
      await assertCanReadResource(
        ctx,
        resource.id,
        "You can only view hover details for your own resource unless you have staff access",
      );
      const directory = await getAnonymizationDirectory(ctx.db);
      const anon = anonymizeResource(resource, directory);
      return {
        id: anon.id,
        displayName: anon.displayName ?? "",
        eid: anon.eid ?? "",
        chapter: resource.chapter,
        lcrCents: resource.lcrCents,
        ucrCents: resource.ucrCents,
        currency: resource.currency,
        chargeabilityTarget: resource.chargeabilityTarget,
        skills: resource.skills as Record<string, unknown>[],
        isActive: resource.isActive,
        resourceType: resource.resourceType,
        areaRole: resource.areaRole,
        country: resource.country,
        managementLevel: resource.managementLevel,
      };
    }),

  getByIdentifier: protectedProcedure
    .input(z.object({ identifier: z.string() }))
    .query(async ({ ctx, input }) => resolveResourceIdentifierSnapshot(ctx, input.identifier)),

  getByIdentifierDetail: protectedProcedure
    .input(z.object({ identifier: z.string() }))
    .query(async ({ ctx, input }) => {
      const resource = await readResourceByIdentifierDetailSnapshot(ctx, input.identifier);
      if ("error" in resource) {
        return resource;
      }
      return mapResourceDetail(resource);
    }),

  getById: protectedProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const resource = await findUniqueOrThrow(
        ctx.db.resource.findUnique({
          where: { id: input.id },
          include: {
            blueprint: true,
            resourceRoles: {
              include: { role: { select: ROLE_BRIEF_SELECT } },
            },
            areaRole: { select: { id: true, name: true } },
          },
        }),
        "Resource",
      );
      await assertCanReadResource(
        ctx,
        resource.id,
        "You can only view your own resource unless you have staff access",
      );

      const directory = await getAnonymizationDirectory(ctx.db);
      return {
        ...anonymizeResource(resource, directory),
        isOwnedByCurrentUser: Boolean(resource.userId && ctx.dbUser?.id && resource.userId === ctx.dbUser.id),
      };
    }),

  getByEid: protectedProcedure
    .input(z.object({ eid: z.string() }))
    .query(async ({ ctx, input }) => {
      const directory = await getAnonymizationDirectory(ctx.db);
      let resource = await ctx.db.resource.findUnique({ where: { eid: input.eid } });
      if (!resource && directory) {
        const resourceId = directory.byAliasEid.get(input.eid.trim().toLowerCase());
        if (resourceId) {
          resource = await ctx.db.resource.findUnique({ where: { id: resourceId } });
        }
      }
      if (!resource) {
        throw new TRPCError({ code: "NOT_FOUND", message: "Resource not found" });
      }
      await assertCanReadResource(
        ctx,
        resource.id,
        "You can only view your own resource unless you have staff access",
      );
      return anonymizeResource(resource, directory);
    }),
};
