import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { Prisma } from "@prisma/client";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { getPrograms } from "@/lib/db/programs";

/**
 * Programs CRUD.
 *
 * Programs are multi-week training plans. The full structure is
 * Program -> ProgramWeek -> ProgramDay -> ProgramExercise. POST
 * accepts the entire tree in a single payload and writes it in
 * one transaction (the v1.1.0:2 AI flow uses the same shape).
 */

const exerciseInput = z.object({
  exerciseId: z.string().min(1),
  order: z.number().int().nonnegative(),
  sets: z.number().int().positive().optional().nullable(),
  repsMin: z.number().int().positive().optional().nullable(),
  repsMax: z.number().int().positive().optional().nullable(),
  rpe: z.number().int().min(1).max(10).optional().nullable(),
  restSeconds: z.number().int().nonnegative().optional().nullable(),
  notes: z.string().optional().nullable(),
});

const dayInput = z.object({
  dayOfWeek: z.number().int().min(0).max(6),
  name: z.string().optional().nullable(),
  description: z.string().optional().nullable(),
  exercises: z.array(exerciseInput),
});

const weekInput = z.object({
  weekNumber: z.number().int().positive(),
  phase: z.string().optional().nullable(),
  description: z.string().optional().nullable(),
  days: z.array(dayInput),
});

const createProgramSchema = z.object({
  name: z.string().min(1),
  description: z.string().optional().nullable(),
  type: z.string().min(1),
  durationWeeks: z.number().int().positive(),
  startDate: z.string(), // ISO
  isActive: z.boolean().optional().default(false),
  weeks: z.array(weekInput).default([]),
});

export async function GET() {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  const programs = await getPrograms(user.id);
  return NextResponse.json(programs);
}

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

    const body = await request.json();
    const validated = createProgramSchema.parse(body);

    // Verify any referenced exerciseIds belong to this user.
    const allExerciseIds = new Set<string>();
    for (const w of validated.weeks)
      for (const d of w.days)
        for (const ex of d.exercises) allExerciseIds.add(ex.exerciseId);

    if (allExerciseIds.size > 0) {
      const owned = await prisma.exercise.findMany({
        where: { userId: user.id, id: { in: Array.from(allExerciseIds) } },
        select: { id: true },
      });
      const ownedIds = new Set(owned.map((e) => e.id));
      const bad = Array.from(allExerciseIds).filter((id) => !ownedIds.has(id));
      if (bad.length > 0) {
        return NextResponse.json(
          { error: "Some exerciseIds don't exist in your library", details: bad },
          { status: 400 },
        );
      }
    }

    const program = await prisma.$transaction(async (tx) => {
      const created = await tx.program.create({
        data: {
          userId: user.id,
          name: validated.name,
          description: validated.description ?? null,
          type: validated.type,
          durationWeeks: validated.durationWeeks,
          startDate: new Date(validated.startDate),
          isActive: validated.isActive,
        },
      });
      for (const w of validated.weeks) {
        const week = await tx.programWeek.create({
          data: {
            programId: created.id,
            weekNumber: w.weekNumber,
            phase: w.phase ?? null,
            description: w.description ?? null,
          },
        });
        for (const d of w.days) {
          const day = await tx.programDay.create({
            data: {
              weekId: week.id,
              dayOfWeek: d.dayOfWeek,
              name: d.name ?? null,
              description: d.description ?? null,
            },
          });
          if (d.exercises.length > 0) {
            await tx.programExercise.createMany({
              data: d.exercises.map((ex) => ({
                dayId: day.id,
                exerciseId: ex.exerciseId,
                order: ex.order,
                sets: ex.sets ?? null,
                repsMin: ex.repsMin ?? null,
                repsMax: ex.repsMax ?? null,
                rpe: ex.rpe ?? null,
                restSeconds: ex.restSeconds ?? null,
                notes: ex.notes ?? null,
              })) as Prisma.ProgramExerciseCreateManyInput[],
            });
          }
        }
      }
      return created;
    });

    return NextResponse.json(program, { status: 201 });
  } catch (err) {
    if (err instanceof z.ZodError) {
      return NextResponse.json(
        { error: "Invalid program payload", details: err.errors },
        { status: 400 },
      );
    }
    console.error("POST /api/programs error:", err);
    return NextResponse.json({ error: "Internal server error" }, { status: 500 });
  }
}
