import '../../domain/domain.dart';
import '../ports.dart';

const int starterSeedVersion = 1;

final class StarterExerciseSeed {
  const StarterExerciseSeed({
    required this.id,
    required this.name,
    required this.description,
    required this.category,
    this.hasTimeMeasure = false,
    this.hasRepsMeasure = false,
    this.hasScoreMeasure = false,
    this.scoreInputMode = ScoreInputMode.manual,
    this.scoreLabel,
    this.scoreUnit,
    this.defaultTargetTimeSeconds,
    this.defaultTargetReps,
    this.defaultTargetScore,
    this.defaultTargetScoreTimeMs,
    this.steps = const [],
  });

  final String id;
  final String name;
  final String description;
  final ExerciseCategory category;
  final bool hasTimeMeasure;
  final bool hasRepsMeasure;
  final bool hasScoreMeasure;
  final ScoreInputMode scoreInputMode;
  final String? scoreLabel;
  final String? scoreUnit;
  final int? defaultTargetTimeSeconds;
  final int? defaultTargetReps;
  final double? defaultTargetScore;
  final int? defaultTargetScoreTimeMs;
  final List<StarterExerciseStepSeed> steps;
}

final class StarterExerciseStepSeed {
  const StarterExerciseStepSeed({
    required this.id,
    required this.name,
    required this.type,
    required this.defaultTargetValue,
  });

  final String id;
  final String name;
  final ExerciseStepType type;
  final int defaultTargetValue;
}

final class StarterProgramSeed {
  const StarterProgramSeed({
    required this.id,
    required this.name,
    required this.defaultRestSeconds,
    required this.exercises,
  });

  final String id;
  final String name;
  final int defaultRestSeconds;
  final List<StarterProgramExerciseSeed> exercises;
}

final class StarterProgramExerciseSeed {
  const StarterProgramExerciseSeed({
    required this.id,
    required this.exerciseId,
    required this.setsCount,
    required this.enabledMeasures,
    this.targetTimeSeconds,
    this.targetReps,
    this.targetScore,
    this.targetScoreTimeMs,
    this.restSecondsOverride,
  });

  final String id;
  final String exerciseId;
  final int setsCount;
  final Set<WorkoutMeasure> enabledMeasures;
  final int? targetTimeSeconds;
  final int? targetReps;
  final double? targetScore;
  final int? targetScoreTimeMs;
  final int? restSecondsOverride;
}

final class StarterWorkoutTemplateSeed {
  const StarterWorkoutTemplateSeed({
    required this.id,
    required this.name,
    required this.programSnapshotId,
  });

  final String id;
  final String name;
  final String programSnapshotId;
}

StarterContent buildStarterContent({
  required List<StarterExerciseSeed> exerciseSeeds,
  required StarterProgramSeed programSeed,
  required StarterWorkoutTemplateSeed templateSeed,
  required DateTime now,
  required String originDeviceId,
}) {
  final exercisesById = <String, Exercise>{};
  for (final seed in exerciseSeeds) {
    exercisesById[seed.id] = Exercise(
      metadata: _metadata(seed.id, now, originDeviceId),
      name: seed.name,
      description: seed.description,
      hasTimeMeasure: seed.hasTimeMeasure,
      hasRepsMeasure: seed.hasRepsMeasure,
      hasScoreMeasure: seed.hasScoreMeasure,
      scoreInputMode: seed.scoreInputMode,
      scoreLabel: seed.scoreLabel,
      scoreUnit: seed.scoreUnit,
      defaultTargetTimeSeconds: seed.defaultTargetTimeSeconds,
      defaultTargetReps: seed.defaultTargetReps,
      defaultTargetScore: seed.defaultTargetScore,
      defaultTargetScoreTimeMs: seed.defaultTargetScoreTimeMs,
      category: seed.category,
      isExample: true,
      steps: [
        for (var index = 0; index < seed.steps.length; index += 1)
          ExerciseStep(
            id: seed.steps[index].id,
            position: index,
            name: seed.steps[index].name,
            type: seed.steps[index].type,
            defaultTargetValue: seed.steps[index].defaultTargetValue,
          ),
      ],
    );
  }

  final programExercises = <ProgramExercise>[];
  for (var index = 0; index < programSeed.exercises.length; index += 1) {
    final seed = programSeed.exercises[index];
    final exercise = exercisesById[seed.exerciseId];
    if (exercise == null) {
      throw DomainException('Unknown starter exercise id: ${seed.exerciseId}');
    }
    programExercises.add(
      ProgramExercise.snapshotFromExercise(
        metadata: _metadata(seed.id, now, originDeviceId),
        programId: programSeed.id,
        exercise: exercise,
        position: index,
        setsCount: seed.setsCount,
        enabledMeasures: seed.enabledMeasures,
        targetTimeSeconds: seed.targetTimeSeconds,
        targetReps: seed.targetReps,
        targetScore: seed.targetScore,
        targetScoreTimeMs: seed.targetScoreTimeMs,
        restSecondsOverride: seed.restSecondsOverride,
      ),
    );
  }

  final program = Program(
    metadata: _metadata(programSeed.id, now, originDeviceId),
    name: programSeed.name,
    defaultRestSeconds: programSeed.defaultRestSeconds,
    isExample: true,
    exercises: programExercises,
  );
  final templateProgram = WorkoutTemplateProgram.snapshotFromProgram(
    metadata: _metadata(templateSeed.programSnapshotId, now, originDeviceId),
    workoutTemplateId: templateSeed.id,
    program: program,
    position: 0,
  );
  final template = WorkoutTemplate(
    metadata: _metadata(templateSeed.id, now, originDeviceId),
    name: templateSeed.name,
    isExample: true,
    programs: [templateProgram],
  );

  return StarterContent(
    exercises: exercisesById.values.toList(growable: false),
    program: program,
    workoutTemplate: template,
  );
}

EntityMetadata _metadata(String id, DateTime now, String originDeviceId) {
  return EntityMetadata(
    id: id,
    createdAt: now,
    updatedAt: now,
    originDeviceId: originDeviceId,
    syncState: SyncState.localOnly,
    localRevision: 0,
  );
}
