import * as mongoose from 'mongoose';
import * as dotenv from 'dotenv';
dotenv.config();

/**
 * Seeds the 3 admin ("default") training plans shown in the app mockups:
 *   1. Full Body Basic
 *   2. Full Body Advanced
 *   3. Upper/Lower Split
 *
 * Exercises are referenced BY NAME (name.en) and resolved to their ObjectId
 * from the already-seeded `exercises` collection — no hard-coded ids. Any name
 * that can't be resolved is reported and that exercise is skipped (the plan is
 * still created without it) so a typo never silently corrupts a plan.
 *
 * A few mockup labels don't exist verbatim in the exercise catalogue and are
 * mapped to the closest seeded exercise:
 *   "Bicep Curls"       -> "Biceps Curl (Barbell/Dumbbell)"
 *   "Tricep Extensions" -> "Overhead Triceps Extension"
 *   "Glute Kickbacks"   -> "Donkey Kicks"
 *
 * Weights/reps/breaks are taken from the mockups (best effort) and are easy to
 * tweak below. `reps` is a single number — for a displayed range we store the
 * top of the range (e.g. "8-12" -> 12); `weight` in kg; `break_duration` in
 * seconds. All under the numeric TrainingSet.values map keyed by field keys.
 */

type SetValues = { reps?: number; weight?: number; break_duration?: number };
type PlanExerciseDef = { name: string; sets: number; values: SetValues };
type PlanDayDef = { name: { en: string; de: string }; exercises: PlanExerciseDef[] };
type PlanDef = {
  name: { en: string; de: string };
  subtitle: { en: string; de: string };
  description: { en: string; de: string };
  days: PlanDayDef[];
};

// 2 min / 3 min breaks in seconds.
const BREAK_2M = 120;
const BREAK_3M = 180;

const plans: PlanDef[] = [
  /* ── 1. Full Body Basic ─────────────────────────────────────────────── */
  {
    name: { en: 'Full Body Basic', de: 'Full Body Basic' },
    subtitle: {
      en: 'Machine-focused for safe strength building. Perfect for 2-3 trainings per week.',
      de: 'Maschinenfokussiert für sicheren Kraftaufbau. Perfekt für 2-3 Trainings pro Woche.',
    },
    description: {
      en: '2 varied sessions per week for all major muscle groups on guided machines. Ideal for 2-3 workouts per week.',
      de: '2 abwechslungsreiche Einheiten pro Woche für alle großen Muskelgruppen an geführten Maschinen. Optimal bei 2-3x Training pro Woche.',
    },
    days: [
      {
        name: { en: 'Day 1', de: 'Tag 1' },
        exercises: [
          { name: 'Leg Press',            sets: 3, values: { reps: 12, weight: 50, break_duration: BREAK_2M } },
          { name: 'Lat Pulldown',         sets: 3, values: { reps: 12, weight: 25, break_duration: BREAK_2M } },
          { name: 'Back Extension',       sets: 3, values: { reps: 12, weight: 5,  break_duration: BREAK_2M } },
          { name: 'Chest Press (Machine)',sets: 3, values: { reps: 12, weight: 15, break_duration: BREAK_2M } },
          { name: 'Shoulder Press (Dumbbell/Machine)', sets: 3, values: { reps: 12, weight: 9, break_duration: BREAK_2M } },
          { name: 'Ab Crunch Machine',    sets: 3, values: { reps: 12, weight: 15, break_duration: BREAK_2M } },
        ],
      },
      {
        name: { en: 'Day 2', de: 'Tag 2' },
        exercises: [
          { name: 'Leg Extension',              sets: 3, values: { reps: 12, weight: 20, break_duration: BREAK_2M } },
          { name: 'Leg Curl (Lying or Seated)', sets: 3, values: { reps: 12, weight: 15, break_duration: BREAK_2M } },
          { name: 'Abduction (Machine)',        sets: 3, values: { reps: 12, weight: 15, break_duration: BREAK_2M } },
          { name: 'Seated Row (Cable)',         sets: 3, values: { reps: 12, weight: 15, break_duration: BREAK_2M } },
          { name: 'Chest Fly (Machine)',        sets: 3, values: { reps: 12, weight: 15, break_duration: BREAK_2M } },
          { name: 'Crunches',                   sets: 3, values: { reps: 12,  break_duration: BREAK_2M } },
        ],
      },
    ],
  },

  /* ── 2. Full Body Advanced ──────────────────────────────────────────── */
  {
    name: { en: 'Full Body Advanced', de: 'Full Body Advanced' },
    subtitle: {
      en: 'Intensive training with free compound movements. Perfect for 2-3 workouts per week.',
      de: 'Intensives Training mit freien Verbundübungen. Perfekt für 2-3 Workouts pro Woche.',
    },
    description: {
      en: '2 intensive sessions per week for all major muscle groups with a focus on free compound movements. Perfect for 2-3 workouts per week.',
      de: '2 intensive Einheiten pro Woche für alle großen Muskelgruppen mit Fokus auf freie Verbundübungen. Perfekt für 2-3 Workouts pro Woche.',
    },
    days: [
      {
        name: { en: 'Day 1', de: 'Tag 1' },
        exercises: [
          { name: 'Squat',                     sets: 3, values: { reps: 6,  weight: 40, break_duration: BREAK_3M } },
          { name: 'Glute Bridge / Hip Thrust', sets: 3, values: { reps: 10, weight: 40, break_duration: BREAK_2M } },
          { name: 'Lat Pulldown',              sets: 3, values: { reps: 10, weight: 20, break_duration: BREAK_2M } },
          { name: 'Incline Bench Press',       sets: 3, values: { reps: 10, weight: 20, break_duration: BREAK_2M } },
          { name: 'Shoulder Press (Dumbbell/Machine)', sets: 3, values: { reps: 10, weight: 8, break_duration: BREAK_2M } },
          { name: 'Ab Crunch Machine',         sets: 3, values: { reps: 12, weight: 10, break_duration: BREAK_2M } },
        ],
      },
      {
        name: { en: 'Day 2', de: 'Tag 2' },
        exercises: [
          { name: 'Deadlift',            sets: 3, values: { reps: 6,  weight: 60, break_duration: BREAK_3M } },
          { name: 'Leg Extension',       sets: 3, values: { reps: 12, weight: 25, break_duration: BREAK_2M } },
          { name: 'Seated Row (Cable)',  sets: 3, values: { reps: 10, weight: 30, break_duration: BREAK_2M } },
          { name: 'Reverse Fly (Machine)', sets: 3, values: { reps: 12, weight: 15, break_duration: BREAK_2M } },
          { name: 'Bench Press',         sets: 3, values: { reps: 10, weight: 20, break_duration: BREAK_2M } },
          { name: 'Crunches',            sets: 3, values: { reps: 15,  break_duration: BREAK_2M } },
        ],
      },
    ],
  },

  /* ── 3. Upper/Lower Split ───────────────────────────────────────────── */
  {
    name: { en: 'Upper/Lower Split', de: 'Upper/Lower Split' },
    subtitle: {
      en: 'The perfect mix of focus and recovery. Separate upper and lower body sessions. Ideal for 4 workouts per week.',
      de: 'Die perfekte Mischung aus Fokus und Erholung. Separate Ober- und Unterkörper-Einheiten. Ideal für 4 Workouts pro Woche.',
    },
    description: {
      en: 'Your training alternates between upper and lower body. Ideal for delivering intensive stimuli 4+ times per week.',
      de: 'Dein Training wechselt zwischen Ober- und Unterkörper. Ideal für intensive Reize 4+ Mal pro Woche.',
    },
    days: [
      {
        name: { en: 'Upper body', de: 'Oberkörper' },
        exercises: [
          { name: 'Bench Press',        sets: 3, values: { reps: 8,  weight: 22, break_duration: BREAK_3M } },
          { name: 'Lat Pulldown',       sets: 3, values: { reps: 10, weight: 20, break_duration: BREAK_2M } },
          { name: 'Seated Row (Cable)', sets: 3, values: { reps: 10, weight: 20, break_duration: BREAK_2M } },
          { name: 'Shoulder Press (Dumbbell/Machine)', sets: 3, values: { reps: 10, weight: 8, break_duration: BREAK_2M } },
          { name: 'Chest Fly (Machine)',sets: 3, values: { reps: 12, weight: 5,  break_duration: BREAK_2M } },
          { name: 'Biceps Curl (Barbell/Dumbbell)', sets: 3, values: { reps: 12, weight: 8, break_duration: BREAK_2M } }, // "Bicep Curls"
          { name: 'Overhead Triceps Extension',     sets: 3, values: { reps: 12, weight: 5, break_duration: BREAK_2M } }, // "Tricep Extensions"
        ],
      },
      {
        name: { en: 'Lower body', de: 'Unterkörper' },
        exercises: [
          { name: 'Squat',                     sets: 3, values: { reps: 8,  weight: 22, break_duration: BREAK_3M } },
          { name: 'Romanian Deadlift (RDL)',   sets: 3, values: { reps: 10, weight: 20, break_duration: BREAK_2M } },
          { name: 'Glute Bridge / Hip Thrust', sets: 3, values: { reps: 10, weight: 40, break_duration: BREAK_2M } },
          { name: 'Bulgarian Split Squat',     sets: 3, values: { reps: 12, weight: 10, break_duration: BREAK_2M } },
          { name: 'Adduction (Machine)',       sets: 3, values: { reps: 12, weight: 22, break_duration: BREAK_2M } },
          { name: 'Donkey Kicks',              sets: 3, values: { reps: 15,  break_duration: BREAK_2M } }, // "Glute Kickbacks"
        ],
      },
    ],
  },
];

// Expand a "3 sets of X" definition into an array of identical set entries,
// matching how the schema stores one entry per set (array length = set count).
function buildSets(def: PlanExerciseDef) {
  return Array.from({ length: def.sets }, () => ({ values: { ...def.values } }));
}

async function run() {
  const uri = process.env.MONGO_URI || 'mongodb://localhost:27017/lifther';
  await mongoose.connect(uri);
  const db = mongoose.connection.db;
  console.log('Connected to DB:', uri);

  /* ── 1. Resolve the admin user used as `createdBy` ── */
  // Priority: SEED_ADMIN_ID env → first super_admin/admin user in the DB.
  let createdBy: mongoose.Types.ObjectId | null = null;
  if (process.env.SEED_ADMIN_ID) {
    createdBy = new mongoose.Types.ObjectId(process.env.SEED_ADMIN_ID);
    console.log(`Using SEED_ADMIN_ID as createdBy: ${createdBy}`);
  } else {
    const adminRoles = await db
      .collection('roles')
      .find({ name: { $in: ['super_admin', 'admin'] } })
      .toArray();
    const roleIds = adminRoles.map((r) => r._id);
    const adminUser = roleIds.length
      ? await db.collection('users').findOne({ role_id: { $in: roleIds } })
      : null;
    if (adminUser) {
      createdBy = adminUser._id;
      console.log(`Using admin user as createdBy: ${adminUser.email ?? adminUser._id}`);
    }
  }

  if (!createdBy) {
    console.error(
      '❌ No admin user found. Create a super_admin/admin user first, or run with SEED_ADMIN_ID=<userId>.',
    );
    await mongoose.disconnect();
    process.exit(1);
  }

  /* ── 2. Build an exercise name.en → _id lookup ── */
  const exerciseDocs = await db
    .collection('exercises')
    .find({ deleted_at: null }, { projection: { 'name.en': 1 } })
    .toArray();
  const exerciseMap = new Map<string, mongoose.Types.ObjectId>();
  for (const ex of exerciseDocs) exerciseMap.set(ex.name?.en, ex._id);

  /* ── 3. Insert each plan (idempotent by name.en) ── */
  console.log('\nSeeding training plans...');
  let inserted = 0;
  let skipped = 0;
  const missing = new Set<string>();

  for (const plan of plans) {
    const exists = await db
      .collection('trainingplans')
      .findOne({ 'name.en': plan.name.en, deleted_at: null });
    if (exists) {
      console.log(`  ⏭️  Plan exists: ${plan.name.en}`);
      skipped++;
      continue;
    }

    const days = plan.days.map((day) => ({
      _id: new mongoose.Types.ObjectId(), // stable id per day (schema uses _id: true)
      name: day.name,
      exercises: day.exercises
        .map((ex) => {
          const exercise_id = exerciseMap.get(ex.name);
          if (!exercise_id) {
            missing.add(ex.name);
            console.warn(`  ⚠️  Exercise not found, skipping: "${ex.name}" (plan "${plan.name.en}")`);
            return null;
          }
          return { exercise_id, sets: buildSets(ex) };
        })
        .filter(Boolean),
    }));

    await db.collection('trainingplans').insertOne({
      name: plan.name,
      subtitle: plan.subtitle,
      description: plan.description,
      days,
      status: 1,
      deleted_at: null,
      createdBy,
      createdByType: 'admin',
      createdAt: new Date(),
      updatedAt: new Date(),
    });
    inserted++;
    console.log(`  ✅ ${plan.name.en} (${days.length} days)`);
  }

  console.log(`\nDone! Plans — inserted: ${inserted}, skipped: ${skipped}`);
  if (missing.size) {
    console.warn(
      `\n⚠️  ${missing.size} exercise name(s) were not found and got skipped:\n   - ${[...missing].join('\n   - ')}\n   Add them to the exercise seeder or fix the names in this file, then re-run.`,
    );
  }
  await mongoose.disconnect();
}

run().catch((err) => {
  console.error('Seeder error:', err);
  process.exit(1);
});
