import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../shared/data/local/app_database.dart';
import '../../../shared/domain/entities/exercise.dart';
import '../../../shared/domain/entities/workout_set.dart';
import '../../../shared/domain/logic/wendler_calculator.dart';

final workoutGeneratorServiceProvider =
    Provider<WorkoutGeneratorService>((ref) {
  return WorkoutGeneratorService();
});

class WorkoutGeneratorService {
  List<Exercise> generateWorkout({
    required int week,
    required int day,
    required Map<String, double> trainingMaxes,
    required UserCollection user,
    required AccessoryTemplate template,
    int? conditioningSets,
  }) {
    final exercises = <Exercise>[];

    exercises.addAll(_generateMainLifts(week, day, trainingMaxes, user));

    if (template == AccessoryTemplate.hypertrophy) {
      exercises
          .addAll(_generateHypertrophyAccessories(day, trainingMaxes, user));
    } else if (template == AccessoryTemplate.conditioning) {
      final sets = (conditioningSets != null && conditioningSets > 0)
          ? conditioningSets
          : 15;
      exercises.addAll(_generateConditioning(day, sets));
    } else if (template == AccessoryTemplate.journeyPullup) {
      exercises.addAll(_generatePullUpJourney(day, trainingMaxes));
    }

    return exercises;
  }

  List<Exercise> _generateMainLifts(int week, int day,
      Map<String, double> trainingMaxes, UserCollection user) {
    final exercises = <Exercise>[];
    final variants = user.exerciseVariants ?? {};

    (String, String, ExerciseType) resolveVariant(String slot, String defaultId,
        String defaultName, ExerciseType defaultType) {
      final variant = variants[slot];
      if (slot == 'pull') {
        if (variant == 'row') return ('row', 'Pendlay Row', ExerciseType.row);
        return ('pullup', 'Weighted Pull-up', ExerciseType.pullup);
      }
      if (slot == 'push') {
        if (variant == 'bench') {
          return ('bench', 'Bench Press', ExerciseType.bench);
        }
        return ('dip', 'Weighted Dip', ExerciseType.dip);
      }
      return (defaultId, defaultName, defaultType);
    }

    void addExercise(String slot, String defaultId, String defaultName,
        ExerciseType defaultType, bool isMain) {
      final (id, name, type) =
          resolveVariant(slot, defaultId, defaultName, defaultType);

      final tm = trainingMaxes[defaultId] ?? 0.0;
      List<WorkoutSet> sets;

      if (isMain) {
        sets = WendlerCalculator.generateSets(
          week: week,
          trainingMax: tm,
          exerciseType: type,
          currentBodyweight: user.currentBodyweight,
        );
      } else {
        if (week == 4) return;

        sets = WendlerCalculator.generateFSLSets(
          trainingMax: tm,
          exerciseType: type,
          currentBodyweight: user.currentBodyweight,
        );
      }

      if (sets.isNotEmpty) {
        exercises.add(Exercise(
          exerciseId: id,
          exerciseName: isMain ? name : '$name (FSL)',
          bodyweightAtSession: user.currentBodyweight,
          sets: sets,
        ));
      }
    }

    if (day == 1) {
      addExercise('legs', 'squat', 'Back Squat', ExerciseType.squat, true);
      addExercise(
          'pull', 'pullup', 'Weighted Pull-up', ExerciseType.pullup, false);
    } else if (day == 2) {
      addExercise('push', 'dip', 'Weighted Dip', ExerciseType.dip, true);
      addExercise('legs', 'squat', 'Back Squat', ExerciseType.squat, false);
    } else if (day == 3) {
      addExercise(
          'pull', 'pullup', 'Weighted Pull-up', ExerciseType.pullup, true);
      addExercise('push', 'dip', 'Weighted Dip', ExerciseType.dip, false);
    }

    return exercises;
  }

  List<Exercise> _generateHypertrophyAccessories(
      int day, Map<String, double> trainingMaxes, UserCollection user) {
    final accessories = <Exercise>[];

    double calculateWeight(double referenceTm, double percentage) {
      final raw = referenceTm * percentage;
      return (raw / 2.5).round() * 2.5;
    }

    Exercise createSimple(String id, String name, int sets, int reps,
        {double weight = 0.0}) {
      return Exercise(
        exerciseId: id,
        exerciseName: name,
        bodyweightAtSession: 0,
        sets: List.generate(
            sets,
            (i) => WorkoutSet(
                  setNumber: i + 1,
                  repsTarget: reps,
                  targetWeightTotal: weight,
                  repsActual: 0,
                  isAmrap: false,
                  completed: false,
                )),
      );
    }

    final squatTm = trainingMaxes['squat'] ?? 0.0;
    final dipTm = trainingMaxes['dip'] ?? 0.0;
    final pullupTm = trainingMaxes['pullup'] ?? 0.0;

    switch (day) {
      case 1: // Squat Tag
        // RDL: ~40% vom Squat TM
        accessories.add(createSimple('rdl', 'Romanian Deadlift', 3, 10,
            weight: calculateWeight(squatTm, 0.4)));

        accessories.add(_createIntervalExercise(
            id: 'kb_snatch_acc',
            name: 'KB Snatch',
            sets: 10,
            intervalSeconds: 60,
            repsPerSet: 5));
        break;

      case 2: // Dip Tag (Push)
        // OHP: ~30% vom System-Dip-TM (konservativ für 3x10)
        accessories.add(createSimple('ohp', 'Overhead Press', 3, 10,
            weight: calculateWeight(dipTm, 0.3)));

        accessories.add(createSimple('face_pull', 'Band Face Pull', 3, 10));
        accessories.add(createSimple('ab_roll', 'Ab Wheel Rollout', 3, 10));
        break;

      case 3: // Pullup Tag (Pull)
        // Curls: ~20% vom System-Pullup-TM
        accessories.add(createSimple('curl', 'Barbell Curl', 3, 10,
            weight: calculateWeight(pullupTm, 0.2)));

        accessories.add(createSimple('plank', 'Plank (30s)', 3, 30));

        accessories.add(_createIntervalExercise(
            id: 'kb_swing',
            name: '2H KB Swing',
            sets: 10,
            intervalSeconds: 60,
            repsPerSet: 10));
        break;
    }
    return accessories;
  }

  List<Exercise> _generatePullUpJourney(
      int day, Map<String, double> trainingMaxes) {
    final exercises = <Exercise>[];

    Exercise createAccessory(
        String id, String name, ExerciseType type, int sets, int reps,
        {double weight = 0.0}) {
      return Exercise(
        exerciseId: id,
        exerciseName: name,
        bodyweightAtSession: 0,
        sets: List.generate(
            sets,
            (i) => WorkoutSet(
                  setNumber: i + 1,
                  repsTarget: reps,
                  targetWeightTotal: weight,
                  repsActual: 0,
                  isAmrap: false,
                  completed: false,
                )),
      );
    }

    double calculateWeight(double referenceTm, double percentage) {
      final raw = referenceTm * percentage;
      return (raw / 2.5).round() * 2.5;
    }

    switch (day) {
      case 1:
        exercises.add(createAccessory('scap_pull', 'Scapular Pull-Ups',
            ExerciseType.scapularPull, 3, 10));

        exercises.add(createAccessory(
            'plank', 'Core Plank (45s)', ExerciseType.plank, 3, 45));
        break;

      case 2:
        exercises.add(createAccessory(
            'inv_row', 'Australian Pull-Ups', ExerciseType.invertedRow, 4, 8));

        exercises.add(createAccessory(
            'face_pull', 'Band Face Pull', ExerciseType.facePull, 3, 15));
        break;

      case 3:
        exercises.add(createAccessory('neg_pull', 'Negative Pull-Ups (5s slow)',
            ExerciseType.negativePullup, 3, 4));

        final rowTm = trainingMaxes['row'] ?? 0.0;
        final curlWeight = rowTm > 0 ? calculateWeight(rowTm, 0.3) : 0.0;

        exercises.add(createAccessory(
            'curl', 'Barbell Curl', ExerciseType.curlBarbell, 3, 10,
            weight: curlWeight));
        break;
    }

    return exercises;
  }

  List<Exercise> _generateConditioning(int day, int targetSets) {
    final accessories = <Exercise>[];

    const totalTimeSeconds = 20 * 60;
    final intervalSeconds = (totalTimeSeconds / targetSets).floor();

    String id;
    String name;

    switch (day) {
      case 1:
        id = 'kb_clean_press';
        name = 'KB Clean & Press';
        break;
      case 2:
        id = 'kb_snatch_cond';
        name = 'KB Snatch';
        break;
      case 3:
        id = 'kb_thruster';
        name = 'KB Thruster';
        break;
      default:
        return [];
    }

    accessories.add(_createIntervalExercise(
      id: id,
      name: name,
      sets: targetSets,
      intervalSeconds: intervalSeconds,
      repsPerSet: 5,
    ));

    return accessories;
  }

  Exercise _createIntervalExercise({
    required String id,
    required String name,
    required int sets,
    required int intervalSeconds,
    required int repsPerSet,
  }) {
    return Exercise(
      exerciseId: id,
      exerciseName: '$name (${_formatIntervalName(intervalSeconds)})',
      bodyweightAtSession: 0,
      intervalSeconds: intervalSeconds,
      sets: List.generate(
          sets,
          (i) => WorkoutSet(
                setNumber: i + 1,
                repsTarget: repsPerSet,
                targetWeightTotal: 0,
                repsActual: 0,
                isAmrap: false,
                completed: false,
              )),
    );
  }

  String _formatIntervalName(int seconds) {
    if (seconds == 60) return 'EMOM';
    return 'E${seconds}S';
  }
}
