import { describe, it, expect, beforeEach } from 'vitest';

import { prisma } from '@/lib/prisma';
import { applyAIProgram } from '@/lib/ai/apply';
import type { AIProgram } from '@/lib/ai/programSchema';

beforeEach(async () => {
  await prisma.session.deleteMany();
  await prisma.setLog.deleteMany();
  await prisma.workout.deleteMany();
  await prisma.programExercise.deleteMany();
  await prisma.programDay.deleteMany();
  await prisma.programWeek.deleteMany();
  await prisma.program.deleteMany();
  await prisma.exercise.deleteMany();
  await prisma.aIGeneration.deleteMany();
  await prisma.aIPromptTemplate.deleteMany();
  await prisma.user.deleteMany();
  await prisma.instanceSettings.deleteMany();
});

async function setup() {
  const user = await prisma.user.create({
    data: { email: 'a@x', passwordHash: 'fake' },
  });
  const bench = await prisma.exercise.create({
    data: {
      userId: user.id,
      name: 'Bench Press',
      type: 'barbell',
      muscleGroups: '[]',
    },
  });
  const squat = await prisma.exercise.create({
    data: {
      userId: user.id,
      name: 'Squat',
      type: 'barbell',
      muscleGroups: '[]',
    },
  });
  return { user, bench, squat };
}

describe('applyAIProgram', () => {
  it('materializes a valid AIProgram into Program + nested rows + flips aiGenerated', async () => {
    const { user, bench, squat } = await setup();
    const ai: AIProgram = {
      name: 'AI 4-week',
      description: 'Test',
      type: 'hypertrophy',
      durationWeeks: 4,
      weeks: [
        {
          weekNumber: 1,
          phase: 'Volume',
          days: [
            {
              dayOfWeek: 1,
              name: 'Push',
              exercises: [
                {
                  exerciseId: bench.id,
                  exerciseName: 'Bench Press',
                  order: 0,
                  sets: 4,
                  repsMin: 6,
                  repsMax: 10,
                  rpe: 8,
                },
              ],
              description: null,
            },
          ],
          description: null,
        },
        {
          weekNumber: 2,
          days: [
            {
              dayOfWeek: 2,
              exercises: [
                {
                  exerciseId: squat.id,
                  exerciseName: 'Squat',
                  order: 0,
                  sets: 5,
                  repsMin: 5,
                  repsMax: 5,
                  rpe: 9,
                  restSeconds: 180,
                  notes: 'Pause for 1 second',
                },
              ],
              name: null,
              description: null,
            },
          ],
          phase: null,
          description: null,
        },
      ],
    };

    const result = await applyAIProgram(prisma, ai, {
      userId: user.id,
      startDate: new Date('2026-05-10'),
      isActive: false,
    });
    expect(result.weeksCreated).toBe(2);
    expect(result.daysCreated).toBe(2);
    expect(result.exercisesCreated).toBe(2);

    const program = await prisma.program.findUnique({
      where: { id: result.programId },
      include: {
        weeks: {
          include: { days: { include: { exercises: true } } },
        },
      },
    });
    expect(program?.aiGenerated).toBe(true);
    expect(program?.weeks).toHaveLength(2);
    expect(program?.weeks[1].days[0].exercises[0].notes).toBe(
      'Pause for 1 second',
    );
  });

  it('rejects when an exerciseId references another user', async () => {
    const { user } = await setup();
    const otherUser = await prisma.user.create({
      data: { email: 'b@x', passwordHash: 'fake' },
    });
    const otherEx = await prisma.exercise.create({
      data: {
        userId: otherUser.id,
        name: 'Other ex',
        type: 'barbell',
        muscleGroups: '[]',
      },
    });
    const ai: AIProgram = {
      name: 'X',
      type: 'h',
      durationWeeks: 1,
      weeks: [
        {
          weekNumber: 1,
          days: [
            {
              dayOfWeek: 1,
              exercises: [
                {
                  exerciseId: otherEx.id,
                  exerciseName: 'Other ex',
                  order: 0,
                  sets: 3,
                },
              ],
            },
          ],
        },
      ],
    };
    await expect(
      applyAIProgram(prisma, ai, {
        userId: user.id,
        startDate: new Date(),
      }),
    ).rejects.toThrow(/don't belong to this user/);
  });

  it('rejects when any exercise still has null exerciseId (unresolved)', async () => {
    const { user, bench } = await setup();
    const ai: AIProgram = {
      name: 'X',
      type: 'h',
      durationWeeks: 1,
      weeks: [
        {
          weekNumber: 1,
          days: [
            {
              dayOfWeek: 1,
              exercises: [
                {
                  exerciseId: bench.id,
                  exerciseName: 'Bench Press',
                  order: 0,
                  sets: 4,
                },
                {
                  exerciseId: null,
                  exerciseName: 'Bulgarian Split Squat',
                  order: 1,
                  sets: 3,
                },
              ],
            },
          ],
        },
      ],
    };
    await expect(
      applyAIProgram(prisma, ai, {
        userId: user.id,
        startDate: new Date(),
      }),
    ).rejects.toThrow(/unresolved/);
    // Nothing should have been written.
    expect(await prisma.program.count()).toBe(0);
  });

  it('honors isActive flag', async () => {
    const { user, bench } = await setup();
    const ai: AIProgram = {
      name: 'Active program',
      type: 'h',
      durationWeeks: 1,
      weeks: [
        {
          weekNumber: 1,
          days: [
            {
              dayOfWeek: 1,
              exercises: [
                {
                  exerciseId: bench.id,
                  exerciseName: 'Bench Press',
                  order: 0,
                  sets: 3,
                },
              ],
            },
          ],
        },
      ],
    };
    const result = await applyAIProgram(prisma, ai, {
      userId: user.id,
      startDate: new Date(),
      isActive: true,
    });
    const p = await prisma.program.findUnique({
      where: { id: result.programId },
    });
    expect(p?.isActive).toBe(true);
  });
});
