import { describe, it, expect, beforeEach } from 'vitest';
import { prisma } from '@/lib/prisma';
import {
  buildHistorySummary,
  formatHistoryContext,
} from '@/lib/ai/historyContext';

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 };
}

async function logWorkout(
  userId: string,
  daysAgo: number,
  sets: Array<{ exerciseId: string; reps: number; weight: number; rpe?: number }>,
) {
  const date = new Date(Date.now() - daysAgo * 86_400_000);
  return prisma.workout.create({
    data: {
      userId,
      date,
      setLogs: {
        create: sets.map((s, i) => ({
          exerciseId: s.exerciseId,
          setNumber: i + 1,
          reps: s.reps,
          weight: s.weight,
          rpe: s.rpe,
        })),
      },
    },
  });
}

describe('buildHistorySummary', () => {
  it('returns empty summary for a user with no workouts', async () => {
    const user = await prisma.user.create({
      data: { email: 'a@x', passwordHash: 'fake' },
    });
    const s = await buildHistorySummary(prisma, user.id);
    expect(s.totalWorkouts).toBe(0);
    expect(s.exercises).toEqual([]);
  });

  it('summarizes a single user\'s recent activity', async () => {
    const { user, bench, squat } = await setup();
    // 3 bench sessions, 2 squat sessions in last 30 days
    await logWorkout(user.id, 1, [
      { exerciseId: bench.id, reps: 5, weight: 225 },
      { exerciseId: bench.id, reps: 5, weight: 225 },
    ]);
    await logWorkout(user.id, 4, [
      { exerciseId: bench.id, reps: 5, weight: 235 },
      { exerciseId: bench.id, reps: 5, weight: 235 },
    ]);
    await logWorkout(user.id, 7, [
      { exerciseId: bench.id, reps: 5, weight: 215 },
      { exerciseId: squat.id, reps: 5, weight: 315 },
    ]);
    await logWorkout(user.id, 14, [
      { exerciseId: squat.id, reps: 5, weight: 305 },
    ]);

    const s = await buildHistorySummary(prisma, user.id);
    expect(s.totalWorkouts).toBe(4);
    expect(s.workoutsPerWeek).toBeGreaterThan(0);
    expect(s.exercises).toHaveLength(2);

    const benchSummary = s.exercises.find((e) => e.name === 'Bench Press');
    expect(benchSummary).toBeTruthy();
    expect(benchSummary!.totalSets).toBe(5);
    expect(benchSummary!.distinctWorkouts).toBe(3);
    expect(benchSummary!.bestWeight).toBe(235);
    expect(benchSummary!.daysSinceLast).toBeLessThanOrEqual(2); // logged 1 day ago

    // Epley(235, 5) = 235 * (1 + 5/30) = 274.17 → 274
    expect(benchSummary!.estimated1RM).toBe(274);
  });

  it('flags stagnation on a stuck exercise', async () => {
    const { user, bench } = await setup();
    // 6 sessions all at the same weight
    for (let d = 0; d < 6; d++) {
      await logWorkout(user.id, d * 5, [
        { exerciseId: bench.id, reps: 5, weight: 225 },
        { exerciseId: bench.id, reps: 5, weight: 225 },
      ]);
    }
    const s = await buildHistorySummary(prisma, user.id);
    const bs = s.exercises.find((e) => e.name === 'Bench Press');
    expect(bs?.stagnant).toBe(true);
  });

  it('does NOT flag stagnation on a progressing exercise', async () => {
    const { user, bench } = await setup();
    // 6 sessions with progressive weight
    for (let d = 0; d < 6; d++) {
      await logWorkout(user.id, (5 - d) * 7, [
        { exerciseId: bench.id, reps: 5, weight: 200 + d * 10 },
      ]);
    }
    const s = await buildHistorySummary(prisma, user.id);
    const bs = s.exercises.find((e) => e.name === 'Bench Press');
    expect(bs?.stagnant).toBe(false);
  });

  it('excludes workouts outside the window', async () => {
    const { user, bench } = await setup();
    await logWorkout(user.id, 5, [{ exerciseId: bench.id, reps: 5, weight: 225 }]);
    await logWorkout(user.id, 200, [{ exerciseId: bench.id, reps: 5, weight: 200 }]);
    const s = await buildHistorySummary(prisma, user.id, 90);
    expect(s.totalWorkouts).toBe(1);
    expect(s.exercises[0].totalSets).toBe(1);
  });

  it('excludes soft-deleted workouts', async () => {
    const { user, bench } = await setup();
    const w = await logWorkout(user.id, 3, [
      { exerciseId: bench.id, reps: 5, weight: 225 },
    ]);
    await prisma.workout.update({
      where: { id: w.id },
      data: { deletedAt: new Date() },
    });
    const s = await buildHistorySummary(prisma, user.id);
    expect(s.totalWorkouts).toBe(0);
  });

  it('isolates per-user data (does not bleed across users)', async () => {
    const { user, bench } = await setup();
    const otherUser = await prisma.user.create({
      data: { email: 'b@x', passwordHash: 'fake' },
    });
    const otherBench = await prisma.exercise.create({
      data: {
        userId: otherUser.id,
        name: 'Bench Press',
        type: 'barbell',
        muscleGroups: '[]',
      },
    });
    await logWorkout(user.id, 1, [{ exerciseId: bench.id, reps: 5, weight: 225 }]);
    await logWorkout(otherUser.id, 1, [
      { exerciseId: otherBench.id, reps: 100, weight: 999 },
    ]);
    const s = await buildHistorySummary(prisma, user.id);
    expect(s.totalWorkouts).toBe(1);
    expect(s.exercises[0].bestWeight).toBe(225); // not 999
  });
});

describe('formatHistoryContext', () => {
  it('emits a friendly message on empty history', () => {
    const out = formatHistoryContext({
      windowDays: 90,
      totalWorkouts: 0,
      workoutsPerWeek: 0,
      primaryTypes: [],
      exercises: [],
    });
    expect(out).toMatch(/no workouts/);
  });

  it('formats a populated summary into a compact block', () => {
    const out = formatHistoryContext({
      windowDays: 90,
      totalWorkouts: 30,
      workoutsPerWeek: 3.3,
      primaryTypes: ['barbell', 'dumbbell', 'cable'],
      exercises: [
        {
          name: 'Bench Press',
          type: 'barbell',
          totalSets: 36,
          distinctWorkouts: 12,
          daysSinceLast: 2,
          lastWeight: 235,
          lastReps: 5,
          bestWeight: 245,
          estimated1RM: 286,
          avgRpe: 8.5,
          stagnant: false,
        },
      ],
    });
    expect(out).toMatch(/30 workouts/);
    expect(out).toMatch(/Bench Press/);
    expect(out).toMatch(/STAGNANT|RPE/);
  });
});
