import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { participantDocuments } from "~/server/db/schema";
import { TRPCError } from "@trpc/server";
import { env } from "~/env";
import * as Minio from "minio";
import { uuid } from "drizzle-orm/pg-core";
import { eq, desc } from "drizzle-orm";

// Initialize MinIO client
const minioUrl = new URL(env.MINIO_ENDPOINT ?? "http://localhost:9000");

const minioClient = new Minio.Client({
  endPoint: minioUrl.hostname,
  port: parseInt(minioUrl.port) || 9000,
  useSSL: minioUrl.protocol === "https:",
  accessKey: env.MINIO_ACCESS_KEY ?? "minioadmin",
  secretKey: env.MINIO_SECRET_KEY ?? "minioadmin",
});

const BUCKET_NAME = env.MINIO_BUCKET_NAME ?? "hristudio";

// Ensure bucket exists on startup (best effort)
const ensureBucket = async () => {
  try {
    const exists = await minioClient.bucketExists(BUCKET_NAME);
    if (!exists) {
      await minioClient.makeBucket(
        BUCKET_NAME,
        env.MINIO_REGION ?? "us-east-1",
      );
      // Set public policy if needed? For now, keep private and use presigned URLs.
    }
  } catch (e) {
    console.error("Error ensuring MinIO bucket exists:", e);
  }
};
void ensureBucket(); // Fire and forget on load

export const filesRouter = createTRPCRouter({
  // Get a presigned URL for uploading a file
  getPresignedUrl: protectedProcedure
    .input(
      z.object({
        filename: z.string(),
        contentType: z.string(),
        participantId: z.string(),
      }),
    )
    .mutation(async ({ input }) => {
      const fileExtension = input.filename.split(".").pop();
      const uniqueFilename = `${input.participantId}/${crypto.randomUUID()}.${fileExtension}`;

      try {
        const presignedUrl = await minioClient.presignedPutObject(
          BUCKET_NAME,
          uniqueFilename,
          60 * 5, // 5 minutes expiry
        );

        return {
          url: presignedUrl,
          storagePath: uniqueFilename, // Pass this back to client to save in DB after upload
        };
      } catch (error) {
        console.error("Error generating presigned URL:", error);
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: "Failed to generate upload URL",
        });
      }
    }),

  // Get a presigned URL for downloading/viewing a file
  getDownloadUrl: protectedProcedure
    .input(
      z.object({
        storagePath: z.string(),
      }),
    )
    .query(async ({ input }) => {
      try {
        const url = await minioClient.presignedGetObject(
          BUCKET_NAME,
          input.storagePath,
          60 * 60, // 1 hour
        );
        return { url };
      } catch (error) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "File not found or storage error",
        });
      }
    }),

  // Record a successful upload in the database
  registerUpload: protectedProcedure
    .input(
      z.object({
        participantId: z.string(),
        name: z.string(),
        type: z.string().optional(),
        storagePath: z.string(),
        fileSize: z.number().optional(),
      }),
    )
    .mutation(async ({ ctx, input }) => {
      await ctx.db.insert(participantDocuments).values({
        participantId: input.participantId,
        name: input.name,
        type: input.type,
        storagePath: input.storagePath,
        fileSize: input.fileSize,
        uploadedBy: ctx.session.user.id,
      });
    }),

  // List documents for a participant
  listParticipantDocuments: protectedProcedure
    .input(z.object({ participantId: z.string() }))
    .query(async ({ ctx, input }) => {
      return await ctx.db.query.participantDocuments.findMany({
        where: eq(participantDocuments.participantId, input.participantId),
        orderBy: [desc(participantDocuments.createdAt)],
        with: {
          // Optional: join with uploader info if needed
        },
      });
    }),

  // Delete a document
  deleteDocument: protectedProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const doc = await ctx.db.query.participantDocuments.findFirst({
        where: eq(participantDocuments.id, input.id),
      });

      if (!doc) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "Document not found",
        });
      }

      // Delete from database
      await ctx.db
        .delete(participantDocuments)
        .where(eq(participantDocuments.id, input.id));

      // Delete from MinIO (fire and forget or await)
      try {
        await minioClient.removeObject(BUCKET_NAME, doc.storagePath);
      } catch (e) {
        console.error("Failed to delete object from S3:", e);
        // We still consider the operation successful for the user as the DB record is gone.
      }
    }),
});
