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

/**
 * Seeds the `challenges` collection with the three challenge types shown in the
 * design:
 *   - Typ 1 => habit        (daily/weekly consistency goals, fixed duration)
 *   - Typ 2 => performance  (one-off benchmarks, open-ended)
 *   - Typ 3 => volume       (progressive overload goals)
 *
 * All challenges share a single image. The physical file lives at
 * `uploads/challengeLogo/challengeLogo1.png` (committed to the repo — see the
 * `!uploads/challengeLogo/` exception in .gitignore — and served statically
 * under `/uploads/`), and `attachment` stores the absolute URL built from
 * BASE_URL so it works in both dev and prod:
 *   dev  -> http://localhost:3000/uploads/challengeLogo/challengeLogo1.png
 *   prod -> <BASE_URL>/uploads/challengeLogo/challengeLogo1.png
 */

const BASE_URL = (process.env.BASE_URL || 'http://localhost:3000').replace(/\/+$/, '');
const CHALLENGE_IMAGE = `${BASE_URL}/uploads/challengeLogo/challengeLogo1.png`;

type Mode = 'habit' | 'performance' | 'volume';

// Matches GoalMetric in src/common/enums/challenges.enum.ts.
// Count (habit):          sessions (number of completions across the windows)
// Absolute (performance): reps | weight (kg) | duration (sec)
// Relative (volume):      reps_delta | weight_delta (kg) | weight_pct (%) | duration_delta (sec)
type GoalMetricToken =
  | 'sessions'
  | 'reps'
  | 'weight'
  | 'duration'
  | 'reps_delta'
  | 'weight_delta'
  | 'weight_pct'
  | 'duration_delta';

interface Goal {
  metric: GoalMetricToken;
  value: number;
  // 'gte' (default) = higher is better. 'lte' = lower is better, for time
  // trials like "Run 3km in 12min" where you finish by getting the time DOWN.
  direction?: 'gte' | 'lte';
}

interface ChallengeSeed {
  en: string;
  de: string;
  duration: number; // in days; 0 = open-ended
  focus: string; // enum token from `focus` enum
  level: string; // enum token from `level` enum
  type: Mode;
  goal?: Goal; // structured target; for habit it is derived from the cadence
  start?: string; // 'YYYY-MM-DD' for date-based challenges; omitted => anytime

  // HABIT cadence. The challenge is split into consecutive windows of
  // `period_days`, each needing `times_per_period` completions:
  //   daily -> 1/1, once a week -> 7/1, 2x a week -> 7/2, every 2 days -> 2/1
  // The target (goal.value) is derived as times_per_period * (duration / period_days).
  period_days?: number;
  times_per_period?: number;
  allowed_misses?: number; // windows that may be missed before failing; 0 = strict
}

// ── Typ 1 — habit ──────────────────────────────────────────────────────────
// The cadence is read off the challenge name ("daily", "once a week",
// "every two days", "2x a week"); the target follows from it.
const habitChallenges: ChallengeSeed[] = [
  // daily -> one window per day, target == duration
  { en: 'Daily Core', de: 'Täglicher Core', duration: 21, focus: 'strength', level: 'beginner', type: 'habit', period_days: 1, times_per_period: 1, allowed_misses: 1 },
  { en: '7-Day Plank Challenge', de: '7-Tage-Plank-Challenge', duration: 7, focus: 'strength', level: 'beginner', type: 'habit', period_days: 1, times_per_period: 1, allowed_misses: 1 },
  { en: '20 squats + 1 minute plank daily', de: '20 Kniebeugen + 1 Minute Plank täglich', duration: 7, focus: 'strength', level: 'beginner', type: 'habit', period_days: 1, times_per_period: 1, allowed_misses: 1 },
  { en: '20 push-ups daily', de: '20 Liegestütze täglich', duration: 14, focus: 'strength', level: 'beginner', type: 'habit', start: '2026-09-01', period_days: 1, times_per_period: 1, allowed_misses: 1 },
  { en: '15min Daily Mobility', de: '15min tägliche Mobilität', duration: 28, focus: 'mobility', level: 'beginner', type: 'habit', period_days: 1, times_per_period: 1, allowed_misses: 1 },

  // once a week -> one window per 7 days, target == weeks (4)
  { en: 'Run 5km once a week', de: '5km einmal pro Woche laufen', duration: 28, focus: 'cardio', level: 'beginner', type: 'habit', period_days: 7, times_per_period: 1, allowed_misses: 1 },
  { en: 'Run 10km once a week', de: '10km einmal pro Woche laufen', duration: 28, focus: 'cardio', level: 'advanced', type: 'habit', period_days: 7, times_per_period: 1, allowed_misses: 1 },
  { en: 'Run 15km once a week', de: '15km einmal pro Woche laufen', duration: 28, focus: 'cardio', level: 'advanced', type: 'habit', period_days: 7, times_per_period: 1, allowed_misses: 1 },

  // every two days -> 21 / 2 = 10 windows
  { en: '1min wallsit every two days', de: '1min Wandsitzen alle zwei Tage', duration: 21, focus: 'strength', level: 'beginner', type: 'habit', period_days: 2, times_per_period: 1 },
  // 2x a week -> one 7-day window needing two completions
  { en: 'Farmers carry 2x a week 20m, 16kg per side', de: 'Farmers Carry 2x pro Woche, 20m, 16kg pro Seite', duration: 7, focus: 'strength', level: 'advanced', type: 'habit', period_days: 7, times_per_period: 2, allowed_misses: 1 },
];

// ── Typ 2 — performance (absolute benchmarks) ──────────────────────────────
// The target is in the name; `duration` is a DEADLINE (0 = Open, no deadline,
// so it can never fail on time). Progress is the best single attempt.
const performanceChallenges: ChallengeSeed[] = [
  { en: '10 push-ups in a row', de: '10 Liegestütze am Stück', duration: 28, focus: 'strength', level: 'beginner', type: 'performance', goal: { metric: 'reps', value: 10 } },
  { en: '25 push-ups in a row', de: '25 Liegestütze am Stück', duration: 28, focus: 'strength', level: 'advanced', type: 'performance', goal: { metric: 'reps', value: 25 } },
  { en: '40 push-ups in a row', de: '40 Liegestütze am Stück', duration: 28, focus: 'strength', level: 'expert', type: 'performance', goal: { metric: 'reps', value: 40 } },
  { en: '3min Wallsit', de: '3min Wandsitzen', duration: 0, focus: 'strength', level: 'advanced', type: 'performance', goal: { metric: 'duration', value: 180 } },
  { en: '1 pull-up', de: '1 Klimmzug', duration: 0, focus: 'strength', level: 'beginner', type: 'performance', goal: { metric: 'reps', value: 1 } },
  { en: '5 pull-ups', de: '5 Klimmzüge', duration: 56, focus: 'strength', level: 'advanced', type: 'performance', goal: { metric: 'reps', value: 5 } },
  // the only LTE goal: finished by getting the time DOWN to 12min (720s)
  { en: 'Run 3km in 12min', de: '3km in 12min laufen', duration: 14, focus: 'cardio', level: 'advanced', type: 'performance', start: '2026-09-01', goal: { metric: 'duration', value: 720, direction: 'lte' } },
  { en: '40kg squat', de: '40kg Kniebeuge', duration: 84, focus: 'power', level: 'beginner', type: 'performance', goal: { metric: 'weight', value: 40 } },
  { en: '100kg squat', de: '100kg Kniebeuge', duration: 84, focus: 'power', level: 'advanced', type: 'performance', goal: { metric: 'weight', value: 100 } },
  { en: '120kg squat', de: '120kg Kniebeuge', duration: 84, focus: 'power', level: 'expert', type: 'performance', goal: { metric: 'weight', value: 120 } },
  { en: '40kg deadlift', de: '40kg Kreuzheben', duration: 84, focus: 'power', level: 'beginner', type: 'performance', goal: { metric: 'weight', value: 40 } },
  { en: '100kg deadlift', de: '100kg Kreuzheben', duration: 84, focus: 'power', level: 'advanced', type: 'performance', goal: { metric: 'weight', value: 100 } },
  { en: '120kg deadlift', de: '120kg Kreuzheben', duration: 84, focus: 'power', level: 'expert', type: 'performance', goal: { metric: 'weight', value: 120 } },
  { en: '50kg hip thrusts', de: '50kg Hip Thrusts', duration: 0, focus: 'power', level: 'beginner', type: 'performance', goal: { metric: 'weight', value: 50 } },
  { en: '80kg hip thrusts', de: '80kg Hip Thrusts', duration: 0, focus: 'power', level: 'advanced', type: 'performance', goal: { metric: 'weight', value: 80 } },
  { en: '120kg hip thrusts', de: '120kg Hip Thrusts', duration: 0, focus: 'power', level: 'expert', type: 'performance', goal: { metric: 'weight', value: 120 } },
  { en: '30kg bench press', de: '30kg Bankdrücken', duration: 0, focus: 'power', level: 'beginner', type: 'performance', goal: { metric: 'weight', value: 30 } },
  { en: '60kg bench press', de: '60kg Bankdrücken', duration: 0, focus: 'power', level: 'advanced', type: 'performance', goal: { metric: 'weight', value: 60 } },
  // binary "can you do it" — modelled as 1 rep so it scores like the rest
  { en: 'Split', de: 'Spagat', duration: 0, focus: 'mobility', level: 'expert', type: 'performance', goal: { metric: 'reps', value: 1 } },
];

// ── Typ 3 — volume (progressive overload) ──────────────────────────────────
const volumeChallenges: ChallengeSeed[] = [
  { en: 'Squats +10% weight', de: 'Kniebeugen +10% Gewicht', duration: 0, focus: 'power', level: 'advanced', type: 'volume', goal: { metric: 'weight_pct', value: 10 } },
  { en: 'Squats +4 reps', de: 'Kniebeugen +4 Wdh.', duration: 28, focus: 'power', level: 'advanced', type: 'volume', goal: { metric: 'reps_delta', value: 4 } },
  { en: 'Bench press +5kg', de: 'Bankdrücken +5kg', duration: 0, focus: 'power', level: 'advanced', type: 'volume', goal: { metric: 'weight_delta', value: 5 } },
  { en: 'Deadlift +10% weight', de: 'Kreuzheben +10% Gewicht', duration: 0, focus: 'power', level: 'advanced', type: 'volume', goal: { metric: 'weight_pct', value: 10 } },
  { en: 'Pull-up +2 reps', de: 'Klimmzug +2 Wdh.', duration: 3, focus: 'strength', level: 'advanced', type: 'volume', goal: { metric: 'reps_delta', value: 2 } },
  { en: 'Plank +30sec', de: 'Plank +30 Sek.', duration: 10, focus: 'strength', level: 'beginner', type: 'volume', goal: { metric: 'duration_delta', value: 30 } },
];

const allChallenges: ChallengeSeed[] = [
  ...habitChallenges,
  // Only habit challenges go live for now; re-enable these when
  // performance and volume are ready.
  ...performanceChallenges,
   ...volumeChallenges,
];

function addDays(date: Date, days: number): Date {
  const d = new Date(date);
  d.setDate(d.getDate() + days);
  return d;
}

function buildDoc(c: ChallengeSeed, now: Date) {
  const isDate = !!c.start;
  const startDate = isDate ? new Date(`${c.start}T00:00:00.000Z`) : null;
  const endDate = startDate ? addDays(startDate, c.duration) : null;

  // status: derive a sensible enum value from the schedule.
  let status = 'startanytime';
  if (isDate) {
    status = startDate && startDate.getTime() > now.getTime() ? 'comingsoon' : 'active';
  }

  const period_days = c.period_days ?? 1;
  const times_per_period = c.times_per_period ?? 1;

  // HABIT: the target is the number of completions across all windows.
  // windows = duration / period_days, target = times_per_period * windows.
  let goal = c.goal ?? null;
  if (c.type === 'habit' && !goal) {
    // ceil: a trailing partial period still counts as a window (21 days every
    // 2 days = 11 sessions, not 10). Must match resolveHabitCadence.
    const windows = Math.ceil(c.duration / period_days);
    goal = { metric: 'sessions', value: times_per_period * windows };
  }
  // every goal stores a direction; only time trials use 'lte'
  if (goal) goal = { ...goal, direction: goal.direction ?? 'gte' };

  return {
    title: { en: c.en, de: c.de },
    short_desc: { en: c.en, de: c.de },
    description: {
      en: `Take on the "${c.en}" challenge and stay consistent to complete it.`,
      de: `Stelle dich der Challenge „${c.de}“ und bleib dran, um sie abzuschließen.`,
    },
    duration: c.duration,
    goal,
    period_days,
    times_per_period,
    allowed_misses: c.allowed_misses ?? 1,
    // `focus` is a Map<lang, string[]> in the schema; enum tokens are kept
    // identical across languages so the existing focus filters keep matching.
    focus: { en: [c.focus], de: [c.focus] },
    level: { en: c.level, de: c.level },
    status: { en: status, de: status },
    start_type: isDate ? 'date' : 'anytime',
    start_date: startDate,
    end_date: endDate,
    attachment: CHALLENGE_IMAGE,
    type: c.type,
    deleted_at: null,
    createdAt: now,
    updatedAt: now,
  };
}

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);
  console.log('Challenge image URL:', CHALLENGE_IMAGE);
  console.log(`Seeding ${allChallenges.length} challenges...`);

  const now = new Date();
  let inserted = 0;
  let updated = 0;

  for (const c of allChallenges) {
    const doc = buildDoc(c, now);
    const exists = await db
      .collection('challenges')
      .findOne({ 'title.en': c.en, deleted_at: null });

    if (!exists) {
      await db.collection('challenges').insertOne(doc);
      inserted++;
      console.log(`  ✅ Inserted [${c.type}]: ${c.en}`);
    } else {
      // Re-runnable: refresh the definition so newly added fields (goal,
      // period_days, times_per_period, ...) land on already-seeded challenges.
      const { createdAt, ...fields } = doc;
      await db
        .collection('challenges')
        .updateOne({ _id: exists._id }, { $set: { ...fields, updatedAt: now } });
      updated++;
      const t = c.type === 'habit' ? ` (target ${doc.goal?.value})` : '';
      console.log(`  ♻️  Updated [${c.type}]: ${c.en}${t}`);
    }
  }

  console.log(`\nDone! Inserted: ${inserted}, Updated: ${updated}`);
  await mongoose.disconnect();
}

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