import { type Prisma } from "@langfuse/shared";
import { z } from "zod/v4";
import {
  createTRPCRouter,
  protectedProjectProcedure,
} from "@/src/server/api/trpc";
import { throwIfNoProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess";
import { TRPCError } from "@trpc/server";
import { auditLog } from "@/src/features/audit-logs/auditLog";
import { logger } from "@langfuse/shared/src/server";
import {
  CreateLlmSchemaInput,
  DeleteLlmSchemaInput,
  UpdateLlmSchemaInput,
} from "../validation";

export const llmSchemaRouter = createTRPCRouter({
  create: protectedProjectProcedure
    .input(CreateLlmSchemaInput)
    .mutation(async ({ input, ctx }) => {
      try {
        throwIfNoProjectAccess({
          session: ctx.session,
          projectId: input.projectId,
          scope: "llmSchemas:CUD",
        });

        const existingSchema = await ctx.prisma.llmSchema.findUnique({
          where: {
            projectId_name: {
              projectId: input.projectId,
              name: input.name,
            },
          },
        });

        if (existingSchema) {
          throw new TRPCError({
            code: "CONFLICT",
            message:
              "An LLM Schema with this name already exists in this project",
          });
        }

        const llmSchema = await ctx.prisma.llmSchema.create({
          data: {
            projectId: input.projectId,
            name: input.name,
            description: input.description,
            schema: input.schema as Prisma.InputJsonValue,
          },
        });

        await auditLog({
          session: ctx.session,
          resourceType: "project",
          resourceId: llmSchema.id,
          action: "create",
          after: llmSchema,
        });

        return llmSchema;
      } catch (error) {
        logger.error("Failed to create LLM Schema", error);
        if (error instanceof TRPCError) {
          throw error;
        }
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: "Creating LLM Schema failed",
        });
      }
    }),

  getAll: protectedProjectProcedure
    .input(
      z.object({
        projectId: z.string(),
      }),
    )
    .query(async ({ input, ctx }) => {
      try {
        throwIfNoProjectAccess({
          session: ctx.session,
          projectId: input.projectId,
          scope: "llmSchemas:read",
        });

        const llmSchemas = await ctx.prisma.llmSchema.findMany({
          where: {
            projectId: input.projectId,
          },
          orderBy: {
            updatedAt: "desc",
          },
        });

        return llmSchemas;
      } catch (error) {
        logger.error("Failed to get LLM Schemas", error);
        if (error instanceof TRPCError) {
          throw error;
        }
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: "Fetching LLM Schemas failed",
        });
      }
    }),

  update: protectedProjectProcedure
    .input(UpdateLlmSchemaInput)
    .mutation(async ({ input, ctx }) => {
      try {
        throwIfNoProjectAccess({
          session: ctx.session,
          projectId: input.projectId,
          scope: "llmSchemas:CUD",
        });

        const existingSchema = await ctx.prisma.llmSchema.findUnique({
          where: {
            id: input.id,
            projectId: input.projectId,
          },
        });

        if (!existingSchema) {
          throw new TRPCError({
            code: "NOT_FOUND",
            message: "LLM Schema not found",
          });
        }

        const duplicateNameCheck = await ctx.prisma.llmSchema.findFirst({
          where: {
            projectId: input.projectId,
            name: input.name,
            id: {
              not: input.id,
            },
          },
        });

        if (duplicateNameCheck) {
          throw new TRPCError({
            code: "CONFLICT",
            message:
              "Another LLM Schema with this name already exists in this project",
          });
        }

        const updatedSchema = await ctx.prisma.llmSchema.update({
          where: {
            id: input.id,
            projectId: input.projectId,
          },
          data: {
            name: input.name,
            description: input.description,
            schema: input.schema as Prisma.InputJsonValue,
          },
        });

        await auditLog({
          session: ctx.session,
          resourceType: "project",
          resourceId: updatedSchema.id,
          action: "update",
          before: existingSchema,
          after: updatedSchema,
        });

        return updatedSchema;
      } catch (error) {
        logger.error("Failed to update LLM Schema", error);
        if (error instanceof TRPCError) {
          throw error;
        }
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: "Updating LLM Schema failed",
        });
      }
    }),

  delete: protectedProjectProcedure
    .input(DeleteLlmSchemaInput)
    .mutation(async ({ input, ctx }) => {
      try {
        throwIfNoProjectAccess({
          session: ctx.session,
          projectId: input.projectId,
          scope: "llmSchemas:CUD",
        });

        const existingSchema = await ctx.prisma.llmSchema.findUnique({
          where: {
            id: input.id,
            projectId: input.projectId,
          },
        });

        if (!existingSchema) {
          throw new TRPCError({
            code: "NOT_FOUND",
            message: "LLM Schema not found",
          });
        }

        await ctx.prisma.llmSchema.delete({
          where: {
            id: input.id,
            projectId: input.projectId,
          },
        });

        await auditLog({
          session: ctx.session,
          resourceType: "project",
          resourceId: input.id,
          action: "delete",
          before: existingSchema,
        });

        return { success: true };
      } catch (error) {
        logger.error("Failed to delete LLM Schema", error);
        if (error instanceof TRPCError) {
          throw error;
        }
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: "Deleting LLM Schema failed",
        });
      }
    }),
});
