import { NextRequest, NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import {
  parseCSV,
  parseFloatMaybe,
  parseIntMaybe,
  getVariationNote,
  resolveExerciseName,
  parseDate,
} from "@/lib/csvParser";

interface ParsedSet {
  setNumber: number;
  weight?: number;
  weightUnit: string;
  reps?: number;
  durationSeconds?: number;
  distance?: number;
  distanceUnit?: string;
  calories?: number;
  rpe?: number;
  customMetrics?: Record<string, string>;
  notes?: string;
}

interface ParsedExercise {
  exerciseId: string;
  exerciseName: string;
  sourceName?: string;
  unmapped?: boolean;
  sets: ParsedSet[];
}

interface ParsedWorkout {
  date: string;
  exercises: ParsedExercise[];
}

interface ParseResponse {
  workouts: ParsedWorkout[];
  unmapped: string[];
}

export async function POST(request: NextRequest) {
  try {
    const user = await getCurrentUser();
    if (!user) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const formData = await request.formData();
    const file = formData.get("file") as File;

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

    if (!file.name.endsWith(".csv")) {
      return NextResponse.json(
        { error: "File must be a CSV file" },
        { status: 400 }
      );
    }

    const content = await file.text();
    const rows = parseCSV(content);

    if (rows.length === 0) {
      return NextResponse.json(
        { error: "CSV is empty or invalid format" },
        { status: 400 }
      );
    }

    // Get all user exercises for matching
    const exercises = await prisma.exercise.findMany({
      where: { userId: user.id },
      select: { id: true, name: true },
    });

    // Build case-insensitive lookup map
    const exerciseMap = new Map<string, string>();
    for (const ex of exercises) {
      exerciseMap.set(ex.name.toLowerCase(), ex.id);
    }

    // Group rows by date
    const workoutsByDate = new Map<string, Array<Record<string, string>>>();

    const unmappedExercises = new Set<string>();

    for (const row of rows) {
      const date = row.date || row.date_str || row.workout_date || "";
      const exerciseName = row.exercise || row.exercise_name || "";

      if (!date || !exerciseName) {
        continue;
      }

      if (!workoutsByDate.has(date)) {
        workoutsByDate.set(date, []);
      }

      workoutsByDate.get(date)!.push(row);

      // Check if exercise can be resolved
      const resolvedName = resolveExerciseName(exerciseName);
      const isKnown = exerciseMap.has(resolvedName.toLowerCase());
      if (!isKnown) {
        unmappedExercises.add(exerciseName);
      }
    }

    // Build parsed workouts
    const parsedWorkouts: ParsedWorkout[] = [];
    const knownColumns = new Set([
      "date",
      "date_str",
      "workout_date",
      "exercise",
      "exercise_name",
      "set",
      "set_number",
      "weight",
      "weight_unit",
      "weightunit",
      "reps",
      "duration",
      "duration_seconds",
      "duration_minutes",
      "time",
      "time_seconds",
      "time_minutes",
      "distance",
      "distance_unit",
      "distanceunit",
      "calories",
      "rpe",
      "notes",
      "custom_metrics_json",
      "custommetricsjson",
    ]);

    for (const [date, rowsForDate] of workoutsByDate) {
      const exercisesMap = new Map<
        string,
        {
          exerciseId: string;
          exerciseName: string;
          sourceName?: string;
          unmapped?: boolean;
          sets: ParsedSet[];
        }
      >();

      for (const row of rowsForDate) {
        const csvExerciseName = row.exercise || row.exercise_name || "";
        const resolvedName = resolveExerciseName(csvExerciseName);
        const exerciseId = exerciseMap.get(resolvedName.toLowerCase()) || "";
        const isUnmapped = !exerciseId;
        const exerciseKey = isUnmapped
          ? `unmapped:${csvExerciseName.toLowerCase()}`
          : exerciseId;

        if (!exercisesMap.has(exerciseKey)) {
          exercisesMap.set(exerciseKey, {
            exerciseId: exerciseId || "",
            exerciseName: isUnmapped ? csvExerciseName : resolvedName,
            sourceName: csvExerciseName,
            unmapped: isUnmapped,
            sets: [],
          });
        }

        const exerciseData = exercisesMap.get(exerciseKey)!;
        const weight = parseFloatMaybe(row.weight);
        const reps = parseIntMaybe(row.reps);
        let notes = row.notes || "";

        // Detect weight unit from notes
        let weightUnit = row.weight_unit || row.weightunit || "lbs";
        if (notes.toLowerCase().includes("kg")) {
          weightUnit = "kg";
        }

        const durationSeconds =
          parseIntMaybe(row.duration_seconds) ??
          parseIntMaybe(row.time_seconds) ??
          (parseFloatMaybe(row.duration_minutes) !== undefined
            ? Math.round((parseFloatMaybe(row.duration_minutes) || 0) * 60)
            : undefined) ??
          (parseFloatMaybe(row.time_minutes) !== undefined
            ? Math.round((parseFloatMaybe(row.time_minutes) || 0) * 60)
            : undefined) ??
          parseIntMaybe(row.duration) ??
          parseIntMaybe(row.time);

        const distance = parseFloatMaybe(row.distance);
        const distanceUnit = row.distance_unit || row.distanceunit || (distance !== undefined ? "mi" : undefined);
        const calories = parseIntMaybe(row.calories);
        const rpe = parseIntMaybe(row.rpe);

        const customMetrics: Record<string, string> = {};
        const customJson = row.custom_metrics_json || row.custommetricsjson;
        if (customJson) {
          try {
            const parsed = JSON.parse(customJson);
            if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
              for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {
                if (v !== null && v !== undefined && String(v).trim() !== "") {
                  customMetrics[String(k)] = String(v);
                }
              }
            }
          } catch {
            // Ignore malformed JSON field.
          }
        }

        for (const [col, val] of Object.entries(row)) {
          if (!val) continue;
          const normalizedCol = col.toLowerCase().trim();
          if (knownColumns.has(normalizedCol)) continue;
          if (normalizedCol.startsWith("custom_")) {
            const key = normalizedCol.replace(/^custom_/, "");
            if (key) customMetrics[key] = val;
            continue;
          }
          // Treat extra columns as custom metrics too.
          customMetrics[normalizedCol] = val;
        }

        // Add variation note if applicable
        const variationNote = getVariationNote(csvExerciseName);
        if (variationNote) {
          notes = notes
            ? `${notes} (${variationNote})`
            : `(${variationNote})`;
        }

        const setNumber =
          parseIntMaybe(row.set_number) ??
          parseIntMaybe(row.set) ??
          exerciseData.sets.length + 1;

        exerciseData.sets.push({
          setNumber,
          weight,
          weightUnit,
          reps,
          durationSeconds,
          distance,
          distanceUnit,
          calories,
          rpe,
          customMetrics: Object.keys(customMetrics).length > 0 ? customMetrics : undefined,
          notes: notes || undefined,
        });
      }

      const workoutExercises = Array.from(exercisesMap.values());
      if (workoutExercises.length > 0) {
        parsedWorkouts.push({
          date: parseDate(date),
          exercises: workoutExercises,
        });
      }
    }

    // Sort by date ascending (oldest first)
    parsedWorkouts.sort(
      (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
    );

    const response: ParseResponse = {
      workouts: parsedWorkouts,
      unmapped: Array.from(unmappedExercises),
    };

    return NextResponse.json(response);
  } catch (error) {
    console.error("CSV parsing error:", error);
    return NextResponse.json(
      { error: "Failed to parse CSV file" },
      { status: 500 }
    );
  }
}
