import { PermissionKey } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { createDalleClient, isDalleConfigured, loggedAiCall, parseAiError } from "../ai-client.js";
import { findUniqueOrThrow } from "../db/helpers.js";
import { generateGeminiImage, isGeminiConfigured, parseGeminiError } from "../gemini-client.js";
import { validateImageDataUrl } from "../lib/image-validation.js";
import { resolveSystemSettingsRuntime } from "../lib/system-settings-runtime.js";
import { managerProcedure, protectedProcedure, requirePermission } from "../trpc.js";

const MAX_COVER_SIZE = 4 * 1024 * 1024; // 4 MB base64 string length limit (client compresses before upload)

async function readImageGenerationStatus(db: {
  systemSettings: {
    findUnique: (args: { where: { id: string } }) => Promise<Record<string, unknown> | null>;
  };
}) {
  const settings = await db.systemSettings.findUnique({
    where: { id: "singleton" },
  });
  const imageProvider = settings?.["imageProvider"] === "gemini" ? "gemini" : "dalle";
  const configured = imageProvider === "gemini"
    ? isGeminiConfigured(settings)
    : isDalleConfigured(settings);

  return {
    configured,
    provider: imageProvider,
  };
}

export const projectCoverProcedures = {
  generateCover: managerProcedure
    .input(z.object({
      projectId: z.string(),
      prompt: z.string().max(500).optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);

      const project = await findUniqueOrThrow(
        ctx.db.project.findUnique({
          where: { id: input.projectId },
          include: { client: { select: { name: true } } },
        }),
        "Project",
      );

      const settings = await ctx.db.systemSettings.findUnique({
        where: { id: "singleton" },
      });

      const runtimeSettings = resolveSystemSettingsRuntime(settings);
      const imageProvider = runtimeSettings.imageProvider ?? "dalle";
      const useGemini = imageProvider === "gemini" && isGeminiConfigured(runtimeSettings);
      const useDalle = imageProvider === "dalle" && isDalleConfigured(runtimeSettings);

      if (!useGemini && !useDalle) {
        throw new TRPCError({
          code: "PRECONDITION_FAILED",
          message: "No image provider configured. Set up DALL-E or Gemini in Admin → Settings.",
        });
      }

      const clientName = project.client?.name ? ` for ${project.client.name}` : "";
      const basePrompt = `Professional cover art for a 3D automotive visualization project: "${project.name}"${clientName}. Style: cinematic, modern, photorealistic CGI rendering, dramatic lighting, studio environment. No text or typography in the image.`;
      const finalPrompt = input.prompt
        ? `${basePrompt} Additional direction: ${input.prompt}`
        : basePrompt;

      let coverImageUrl: string;

      if (useGemini) {
        try {
          coverImageUrl = await generateGeminiImage(
            runtimeSettings.geminiApiKey!,
            finalPrompt,
            runtimeSettings.geminiModel ?? undefined,
          );
        } catch (err) {
          throw new TRPCError({
            code: "INTERNAL_SERVER_ERROR",
            message: `Gemini error: ${parseGeminiError(err)}`,
          });
        }
      } else {
        const dalleClient = createDalleClient(runtimeSettings);
        const model = runtimeSettings.aiProvider === "azure" ? runtimeSettings.azureDalleDeployment! : "dall-e-3";

        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        let response: any;
        try {
          response = await loggedAiCall("dalle", model, finalPrompt.length, () =>
            dalleClient.images.generate({
              model,
              prompt: finalPrompt,
              size: "1024x1024",
              n: 1,
              response_format: "b64_json",
            }),
          );
        } catch (err) {
          throw new TRPCError({
            code: "INTERNAL_SERVER_ERROR",
            message: `DALL-E error: ${parseAiError(err)}`,
          });
        }

        const b64 = response.data?.[0]?.b64_json;
        if (!b64) {
          throw new TRPCError({
            code: "INTERNAL_SERVER_ERROR",
            message: "No image data returned from DALL-E",
          });
        }

        coverImageUrl = `data:image/png;base64,${b64}`;
      }

      await ctx.db.project.update({
        where: { id: input.projectId },
        data: { coverImageUrl },
      });

      return { coverImageUrl };
    }),

  uploadCover: managerProcedure
    .input(z.object({
      projectId: z.string(),
      imageDataUrl: z.string(),
    }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);

      if (!input.imageDataUrl.startsWith("data:image/")) {
        throw new TRPCError({
          code: "BAD_REQUEST",
          message: "Invalid image format. Must be a data URL starting with 'data:image/'.",
        });
      }

      const magicCheck = validateImageDataUrl(input.imageDataUrl);
      if (!magicCheck.valid) {
        throw new TRPCError({
          code: "BAD_REQUEST",
          message: `File validation failed: ${magicCheck.reason}`,
        });
      }

      if (input.imageDataUrl.length > MAX_COVER_SIZE) {
        throw new TRPCError({
          code: "BAD_REQUEST",
          message: "Image too large. Maximum compressed size is 4 MB.",
        });
      }

      await findUniqueOrThrow(
        ctx.db.project.findUnique({ where: { id: input.projectId } }),
        "Project",
      );

      await ctx.db.project.update({
        where: { id: input.projectId },
        data: { coverImageUrl: input.imageDataUrl },
      });

      return { coverImageUrl: input.imageDataUrl };
    }),

  removeCover: managerProcedure
    .input(z.object({ projectId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);

      await findUniqueOrThrow(
        ctx.db.project.findUnique({ where: { id: input.projectId } }),
        "Project",
      );

      await ctx.db.project.update({
        where: { id: input.projectId },
        data: { coverImageUrl: null },
      });

      return { ok: true };
    }),

  updateCoverFocus: managerProcedure
    .input(z.object({
      projectId: z.string(),
      coverFocusY: z.number().int().min(0).max(100),
    }))
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);
      await ctx.db.project.update({
        where: { id: input.projectId },
        data: { coverFocusY: input.coverFocusY },
      });
      return { ok: true };
    }),

  isImageGenConfigured: protectedProcedure
    .query(async ({ ctx }) => readImageGenerationStatus(ctx.db)),

  /** @deprecated Use isImageGenConfigured instead */
  isDalleConfigured: protectedProcedure
    .query(async ({ ctx }) => {
      const { configured } = await readImageGenerationStatus(ctx.db);
      return { configured };
    }),
};
