import { and, eq } from "drizzle-orm";
import { NextResponse, type NextRequest } from "next/server";
import { headers } from "next/headers";
import { z } from "zod";
import {
  generateFileKey,
  getMimeType,
  uploadFile,
  validateFile,
} from "~/lib/storage/minio";
import { auth } from "~/lib/auth";
import { db } from "~/server/db";
import {
  experiments,
  mediaCaptures,
  studyMembers,
  trials,
} from "~/server/db/schema";

const uploadSchema = z.object({
  trialId: z.string().optional(),
  category: z
    .enum(["video", "audio", "image", "document", "sensor_data"])
    .default("document"),
  filename: z.string(),
  contentType: z.string().optional(),
});

export async function POST(request: NextRequest) {
  try {
    // Check authentication
    const session = await auth.api.getSession({
      headers: await headers(),
    });
    if (!session?.user) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    // Parse form data
    const formData = await request.formData();
    const file = formData.get("file") as File;
    const trialId = formData.get("trialId") as string | null;
    const category = (formData.get("category") as string) || "document";

    if (!file) {
      return NextResponse.json({ error: "No file provided" }, { status: 400 });
    }

    // Validate input
    const validationResult = uploadSchema.safeParse({
      trialId: trialId ?? undefined,
      category,
      filename: file.name,
      contentType: file.type,
    });

    if (!validationResult.success) {
      return NextResponse.json(
        {
          error: "Invalid request parameters",
          details: validationResult.error.flatten(),
        },
        { status: 400 },
      );
    }

    const { trialId: validatedTrialId, category: validatedCategory } =
      validationResult.data;

    // Validate file type and size based on category
    const fileValidation = validateFileByCategory(file, validatedCategory);
    if (!fileValidation.valid) {
      return NextResponse.json(
        { error: fileValidation.error },
        { status: 400 },
      );
    }

    // Check trial access if trialId is provided
    if (validatedTrialId) {
      const trial = await db
        .select({
          id: trials.id,
          studyId: experiments.studyId,
        })
        .from(trials)
        .innerJoin(experiments, eq(trials.experimentId, experiments.id))
        .where(eq(trials.id, validatedTrialId))
        .limit(1);

      if (!trial.length || !trial[0]) {
        return NextResponse.json({ error: "Trial not found" }, { status: 404 });
      }

      // Check if user has access to this trial through study membership
      const membership = await db
        .select()
        .from(studyMembers)
        .where(
          and(
            eq(studyMembers.studyId, trial[0].studyId),
            eq(studyMembers.userId, session.user.id),
          ),
        )
        .limit(1);

      if (!membership.length) {
        return NextResponse.json(
          { error: "Insufficient permissions to upload to this trial" },
          { status: 403 },
        );
      }
    }

    // Generate unique file key
    const fileKey = generateFileKey(
      validatedCategory,
      file.name,
      session.user.id,
      validatedTrialId,
    );

    // Convert file to buffer
    const arrayBuffer = await file.arrayBuffer();
    const buffer = Buffer.from(arrayBuffer);

    // Upload to MinIO
    const uploadResult = await uploadFile({
      key: fileKey,
      body: buffer,
      contentType: file.type || getMimeType(file.name),
      metadata: {
        originalName: file.name,
        uploadedBy: session.user.id,
        uploadedAt: new Date().toISOString(),
        category: validatedCategory,
        ...(validatedTrialId && { trialId: validatedTrialId }),
      },
    });

    // Save media capture record to database
    const mediaCapture = await db
      .insert(mediaCaptures)
      .values({
        trialId: validatedTrialId!, // Non-null assertion since it's validated above
        format: file.type || getMimeType(file.name),
        fileSize: file.size,
        storagePath: fileKey,
        mediaType: getCaptureType(validatedCategory),
        metadata: {
          uploadedBy: session.user.id,
          category: validatedCategory,
          etag: uploadResult.etag,
          originalName: file.name,
        },
      })
      .returning();

    return NextResponse.json({
      success: true,
      data: {
        id: mediaCapture[0]?.id,
        filename: file.name,
        size: file.size,
        contentType: file.type,
        key: fileKey,
        url: uploadResult.url,
        category: validatedCategory,
        uploadedAt: new Date().toISOString(),
      },
    });
  } catch (error) {
    console.error("Upload error:", error);
    return NextResponse.json(
      {
        error: "Upload failed",
        details: error instanceof Error ? error.message : "Unknown error",
      },
      { status: 500 },
    );
  }
}

// Generate presigned upload URL for direct client uploads
export async function GET(request: NextRequest) {
  try {
    const session = await auth.api.getSession({
      headers: await headers(),
    });
    if (!session?.user) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const { searchParams } = new URL(request.url);
    const filename = searchParams.get("filename");
    const contentType = searchParams.get("contentType");
    const category = searchParams.get("category") ?? "document";
    const trialId = searchParams.get("trialId");

    if (!filename) {
      return NextResponse.json(
        { error: "Filename is required" },
        { status: 400 },
      );
    }

    // Validate category
    const validCategories = [
      "video",
      "audio",
      "image",
      "document",
      "sensor_data",
    ];
    if (!validCategories.includes(category)) {
      return NextResponse.json({ error: "Invalid category" }, { status: 400 });
    }

    // Generate unique file key
    const fileKey = generateFileKey(
      category,
      filename,
      session.user.id,
      trialId ?? undefined,
    );

    // Generate presigned URL for upload
    const { getUploadUrl } = await import("~/lib/storage/minio");
    const uploadUrl = await getUploadUrl(fileKey, contentType ?? undefined);

    return NextResponse.json({
      success: true,
      data: {
        uploadUrl,
        fileKey,
        expiresIn: 3600, // 1 hour
      },
    });
  } catch (error) {
    console.error("Presigned URL generation error:", error);
    return NextResponse.json(
      {
        error: "Failed to generate upload URL",
        details: error instanceof Error ? error.message : "Unknown error",
      },
      { status: 500 },
    );
  }
}

function validateFileByCategory(
  file: File,
  category: string,
): { valid: boolean; error?: string } {
  const maxSizes = {
    video: 500 * 1024 * 1024, // 500MB
    audio: 100 * 1024 * 1024, // 100MB
    image: 10 * 1024 * 1024, // 10MB
    document: 50 * 1024 * 1024, // 50MB
    sensor_data: 100 * 1024 * 1024, // 100MB
  };

  const allowedTypes = {
    video: ["mp4", "avi", "mov", "wmv", "flv", "webm"],
    audio: ["mp3", "wav", "ogg", "m4a"],
    image: ["jpg", "jpeg", "png", "gif", "webp", "svg"],
    document: ["pdf", "doc", "docx", "txt", "csv", "json", "xml"],
    sensor_data: ["csv", "json", "txt", "xml"],
  };

  const maxSize =
    maxSizes[category as keyof typeof maxSizes] || 50 * 1024 * 1024;
  const types = allowedTypes[category as keyof typeof allowedTypes] || [];

  return validateFile(file.name, file.size, types, maxSize);
}

function getCaptureType(category: string): "video" | "audio" | "image" {
  switch (category) {
    case "video":
      return "video";
    case "audio":
      return "audio";
    case "image":
      return "image";
    case "sensor_data":
      return "image"; // Map sensor data to image for now
    default:
      return "image"; // Default to image
  }
}
