import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gametime/application/application.dart';
import 'package:gametime/domain/domain.dart';
import 'package:gametime/presentation/presentation.dart';

void main() {
  testWidgets('la liste expose le lien secondaire Progression', (tester) async {
    var opened = false;

    await tester.pumpWidget(
      MaterialApp(
        home: HistoryListScreen(
          historyUseCases: _historyUseCases(_FakeWorkoutHistoryRepository()),
          workoutTemplateUseCases: _workoutTemplateUseCases(),
          activeUseCases: _activeUseCases(),
          closeUseCase: _closeUseCase(),
          onOpenProgression: () => opened = true,
        ),
      ),
    );

    await tester.tap(find.text('Progression'));
    await tester.pump();

    expect(opened, isTrue);
  });

  testWidgets('la liste groupe les séances par période', (tester) async {
    final groups = groupHistoriesByPeriod([
      _history(id: 'today', startedAt: DateTime.utc(2026, 7, 17, 9)),
      _history(id: 'week', startedAt: DateTime.utc(2026, 7, 15, 9)),
      _history(id: 'old', startedAt: DateTime.utc(2026, 7, 1, 9)),
    ], DateTime.utc(2026, 7, 17, 12));

    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          body: Column(children: [for (final key in groups.keys) Text(key)]),
        ),
      ),
    );

    expect(find.text("Aujourd'hui"), findsOneWidget);
    expect(find.text('Cette semaine'), findsOneWidget);
    expect(find.text('Plus ancien'), findsOneWidget);
  });

  testWidgets('le détail affiche temps répétitions et score avec unité', (
    tester,
  ) async {
    await tester.pumpWidget(
      MaterialApp(
        home: HistoryDetailScreen(
          history: _history(id: 'history-1'),
          historyUseCases: _historyUseCases(_FakeWorkoutHistoryRepository()),
          workoutTemplateUseCases: _workoutTemplateUseCases(),
          activeUseCases: _activeUseCases(),
          closeUseCase: _closeUseCase(),
        ),
      ),
    );

    expect(find.text('Programme jambes'), findsOneWidget);
    expect(find.text('Squat'), findsOneWidget);
    expect(
      find.text('Temps 00:45 · 10 répétitions · Score 80 kg'),
      findsOneWidget,
    );
  });

  testWidgets('le détail affiche le score chrono comme temps réalisé', (
    tester,
  ) async {
    await tester.pumpWidget(
      MaterialApp(
        home: HistoryDetailScreen(
          history: _history(id: 'history-1', stopwatchScore: true),
          historyUseCases: _historyUseCases(_FakeWorkoutHistoryRepository()),
          workoutTemplateUseCases: _workoutTemplateUseCases(),
          activeUseCases: _activeUseCases(),
          closeUseCase: _closeUseCase(),
        ),
      ),
    );

    expect(
      find.text(
        'Temps 00:45 · 10 répétitions · '
        'Temps réalisé : 00:38.7 / objectif 00:45',
      ),
      findsOneWidget,
    );
    expect(find.textContaining('Score 38.7'), findsNothing);
  });

  testWidgets('le détail affiche les résultats d’étapes par passage', (
    tester,
  ) async {
    await tester.pumpWidget(
      MaterialApp(
        home: HistoryDetailScreen(
          history: _history(id: 'history-1', withStepResults: true),
          historyUseCases: _historyUseCases(_FakeWorkoutHistoryRepository()),
          workoutTemplateUseCases: _workoutTemplateUseCases(),
          activeUseCases: _activeUseCases(),
          closeUseCase: _closeUseCase(),
        ),
      ),
    );

    expect(find.text('Étapes réalisées'), findsOneWidget);
    expect(find.text('Passage 1'), findsOneWidget);
    expect(
      find.text(
        'Étape 1 · Dribble gauche · Temps 00:10 · Réussites : 4 paniers',
      ),
      findsOneWidget,
    );
    expect(find.text('Étape 2 · Dribble droite'), findsOneWidget);
    expect(find.text('Passée'), findsOneWidget);
  });

  testWidgets('la suppression demande une confirmation', (tester) async {
    final repository = _FakeWorkoutHistoryRepository();

    await tester.pumpWidget(
      MaterialApp(
        home: HistoryDetailScreen(
          history: _history(id: 'history-1'),
          historyUseCases: _historyUseCases(repository),
          workoutTemplateUseCases: _workoutTemplateUseCases(),
          activeUseCases: _activeUseCases(),
          closeUseCase: _closeUseCase(),
        ),
      ),
    );

    await tester.tap(find.text("Supprimer de l'historique"));
    await tester.pumpAndSettle();

    expect(repository.deletedIds, isEmpty);
    expect(find.text("Supprimer de l'historique ?"), findsOneWidget);

    await tester.tap(find.text('Annuler'));
    await tester.pumpAndSettle();

    expect(repository.deletedIds, isEmpty);

    await tester.tap(find.text("Supprimer de l'historique"));
    await tester.pumpAndSettle();
    await tester.tap(find.widgetWithText(FilledButton, 'Supprimer'));
    await tester.pumpAndSettle();

    expect(repository.deletedIds, ['history-1']);
  });
}

WorkoutHistory _history({
  required String id,
  DateTime? startedAt,
  bool stopwatchScore = false,
  bool withStepResults = false,
}) {
  final start = startedAt ?? DateTime.utc(2026, 7, 17, 10);
  return WorkoutHistory(
    metadata: _metadata(id),
    sourceWorkoutTemplateId: 'template-1',
    sourceActiveWorkoutSessionId: 'session-1',
    nameSnapshot: 'Séance jambes',
    startedAt: start,
    endedAt: start.add(const Duration(minutes: 30)),
    totalActiveMs: 1800000,
    completed: true,
    historySnapshotJson: jsonEncode({
      'sessionId': 'session-1',
      'resolvedTemplateSnapshotJson': _resolvedSnapshot(
        stopwatchScore: stopwatchScore,
        withSteps: withStepResults,
      ),
      'results': [
        {
          'programSnapshotId': 'program-snapshot-1',
          'exerciseSnapshotId': 'exercise-snapshot-1',
          'programIndex': 0,
          'exerciseIndex': 0,
          'setIndex': 0,
          'actualTimeMs': 45000,
          'actualReps': 10,
          if (stopwatchScore) 'actualScoreTimeMs': 38700,
          if (!stopwatchScore) 'actualScore': 80,
          'scoreLabelSnapshot': 'Charge',
          'scoreInputModeSnapshot': stopwatchScore
              ? ScoreInputMode.stopwatch.name
              : ScoreInputMode.manual.name,
          if (!stopwatchScore) 'scoreUnitSnapshot': 'kg',
          if (stopwatchScore) 'targetScoreTimeMsSnapshot': 45000,
        },
      ],
    }),
    stepResults: withStepResults
        ? [
            WorkoutHistoryStepResult(
              metadata: _metadata('step-result-1'),
              workoutHistoryId: id,
              programSnapshotId: 'program-snapshot-1',
              exerciseSnapshotId: 'exercise-snapshot-1',
              programIndex: 0,
              exerciseIndex: 0,
              setIndex: 0,
              passageIndex: 0,
              stepIndex: 0,
              stepSnapshotId: 'step-1',
              stepNameSnapshot: 'Dribble gauche',
              stepTypeSnapshot: ExerciseStepType.time,
              targetValueSnapshot: 10,
              hasScoreSnapshot: true,
              scoreInputModeSnapshot: ScoreInputMode.manual,
              scoreLabelSnapshot: 'Réussites',
              scoreUnitSnapshot: 'paniers',
              targetScoreSnapshot: 3,
              status: SetResultStatus.completed,
              actualTimeMs: 10000,
              actualScore: 4,
            ),
            WorkoutHistoryStepResult(
              metadata: _metadata('step-result-2'),
              workoutHistoryId: id,
              programSnapshotId: 'program-snapshot-1',
              exerciseSnapshotId: 'exercise-snapshot-1',
              programIndex: 0,
              exerciseIndex: 0,
              setIndex: 0,
              passageIndex: 0,
              stepIndex: 1,
              stepSnapshotId: 'step-2',
              stepNameSnapshot: 'Dribble droite',
              stepTypeSnapshot: ExerciseStepType.reps,
              targetValueSnapshot: 8,
              hasScoreSnapshot: false,
              status: SetResultStatus.skipped,
            ),
          ]
        : const [],
  );
}

String _resolvedSnapshot({
  bool stopwatchScore = false,
  bool withSteps = false,
}) {
  return jsonEncode({
    'name': 'Séance jambes',
    'programs': [
      {
        'id': 'program-snapshot-1',
        'programNameSnapshot': 'Programme jambes',
        'programSnapshotJson': jsonEncode({
          'exercises': [
            {
              'id': 'exercise-snapshot-1',
              'exerciseNameSnapshot': 'Squat',
              'setsCount': 1,
              'timeEnabled': true,
              'repsEnabled': true,
              'scoreEnabled': true,
              'targetTimeSeconds': 45,
              'targetReps': 10,
              if (stopwatchScore) 'targetScoreTimeMs': 45000,
              if (!stopwatchScore) 'targetScore': 80,
              'scoreInputModeSnapshot': stopwatchScore
                  ? ScoreInputMode.stopwatch.name
                  : ScoreInputMode.manual.name,
              'scoreLabelSnapshot': 'Charge',
              if (!stopwatchScore) 'scoreUnitSnapshot': 'kg',
              'restSecondsOverride': 0,
              if (withSteps)
                'exerciseStepsSnapshot': [
                  {
                    'id': 'step-1',
                    'position': 0,
                    'name': 'Dribble gauche',
                    'type': ExerciseStepType.time.name,
                    'defaultTargetValue': 10,
                    'hasScore': true,
                    'scoreInputMode': ScoreInputMode.manual.name,
                    'scoreLabel': 'Réussites',
                    'scoreUnit': 'paniers',
                    'defaultTargetScore': 3,
                  },
                  {
                    'id': 'step-2',
                    'position': 1,
                    'name': 'Dribble droite',
                    'type': ExerciseStepType.reps.name,
                    'defaultTargetValue': 8,
                    'hasScore': false,
                  },
                ],
            },
          ],
        }),
      },
    ],
    'overrides': const [],
  });
}

EntityMetadata _metadata(String id) {
  return EntityMetadata(
    id: id,
    createdAt: DateTime.utc(2026, 7, 17),
    updatedAt: DateTime.utc(2026, 7, 17),
    originDeviceId: 'device-1',
  );
}

WorkoutHistoryUseCases _historyUseCases(
  _FakeWorkoutHistoryRepository repository,
) {
  return WorkoutHistoryUseCases(
    repository: repository,
    clock: _FakeClock(DateTime.utc(2026, 7, 17)),
  );
}

WorkoutTemplateUseCases _workoutTemplateUseCases() {
  return WorkoutTemplateUseCases(
    templateRepository: _FakeWorkoutTemplateRepository(),
    programRepository: _FakeProgramRepository(),
    clock: _FakeClock(DateTime.utc(2026, 7, 17)),
    ids: _FakeIds(),
    originDeviceId: 'device-1',
  );
}

ActiveWorkoutSessionUseCases _activeUseCases() {
  return ActiveWorkoutSessionUseCases(
    sessionRepository: _FakeActiveSessionRepository(),
    templateRepository: _FakeWorkoutTemplateRepository(),
    clock: _FakeClock(DateTime.utc(2026, 7, 17)),
    ids: _FakeIds(),
    originDeviceId: 'device-1',
  );
}

CloseWorkoutSessionUseCase _closeUseCase() {
  return CloseWorkoutSessionUseCase(
    sessionRepository: _FakeActiveSessionRepository(),
    historyRepository: _FakeWorkoutHistoryRepository(),
    clock: _FakeClock(DateTime.utc(2026, 7, 17)),
    ids: _FakeIds(),
    originDeviceId: 'device-1',
  );
}

final class _FakeClock implements Clock {
  _FakeClock(this.value);

  DateTime value;

  @override
  DateTime now() => value;
}

final class _FakeIds implements IdGenerator {
  var _next = 0;

  @override
  String newId() {
    _next += 1;
    return 'id-$_next';
  }
}

final class _FakeWorkoutHistoryRepository implements WorkoutHistoryRepository {
  final deletedIds = <String>[];

  @override
  Future<void> delete(String id, DateTime deletedAt) async {
    deletedIds.add(id);
  }

  @override
  Future<WorkoutHistory?> findById(String id) async => null;

  @override
  Future<List<WorkoutHistory>> listActive() async => const [];

  @override
  Future<void> save(WorkoutHistory history) async {}

  @override
  Future<void> saveSetResult(WorkoutHistorySetResult result) async {}

  @override
  Future<void> saveStepResult(WorkoutHistoryStepResult result) async {}
}

final class _FakeWorkoutTemplateRepository
    implements WorkoutTemplateRepository {
  @override
  Future<WorkoutTemplate?> findById(String id) async => null;

  @override
  Future<List<WorkoutTemplate>> listActive() async => const [];

  @override
  Future<void> replaceComposition(
    WorkoutTemplate template,
    DateTime deletedAt,
  ) async {}

  @override
  Future<void> save(WorkoutTemplate template) async {}

  @override
  Future<void> saveOverride(WorkoutTemplateExerciseOverride override) async {}

  @override
  Future<void> saveProgram(WorkoutTemplateProgram program) async {}
}

final class _FakeProgramRepository implements ProgramRepository {
  @override
  Future<Program?> findById(String id) async => null;

  @override
  Future<List<Program>> listActive() async => const [];

  @override
  Future<void> replaceExercises(Program program, DateTime deletedAt) async {}

  @override
  Future<void> save(Program program) async {}

  @override
  Future<void> saveExercise(ProgramExercise exercise) async {}
}

final class _FakeActiveSessionRepository implements ActiveSessionRepository {
  @override
  Future<ActiveWorkoutSession?> findById(String id) async => null;

  @override
  Future<ActiveWorkoutSession?> findOpen() async => null;

  @override
  Future<ActiveRestState?> findRestStateById(String id) async => null;

  @override
  Future<ActiveScoreStopwatchState?> findScoreStopwatchState({
    required String sessionId,
    required int programIndex,
    required int exerciseIndex,
    required int setIndex,
  }) async => null;

  @override
  Future<ActiveSetTimerState?> findSetTimerState({
    required String sessionId,
    required int programIndex,
    required int exerciseIndex,
    required int setIndex,
  }) async => null;

  @override
  Future<ActiveExerciseStepProgressState?> findExerciseStepProgressState({
    required String sessionId,
    required int programIndex,
    required int exerciseIndex,
    required int setIndex,
  }) async => null;

  @override
  Future<List<ActiveRestState>> listRestStates(String sessionId) async {
    return const [];
  }

  @override
  Future<List<ActiveScoreStopwatchState>> listScoreStopwatchStates(
    String sessionId,
  ) async {
    return const [];
  }

  @override
  Future<List<ActiveSetTimerState>> listSetTimerStates(String sessionId) async {
    return const [];
  }

  @override
  Future<List<ActiveSetResult>> listSetResults(String sessionId) async {
    return const [];
  }

  @override
  Future<List<ActiveExerciseStepProgressState>> listExerciseStepProgressStates(
    String sessionId,
  ) async {
    return const [];
  }

  @override
  Future<List<ActiveExerciseStepResult>> listExerciseStepResults(
    String sessionId,
  ) async {
    return const [];
  }

  @override
  Future<void> save(ActiveWorkoutSession session) async {}

  @override
  Future<void> saveRestState(ActiveRestState restState) async {}

  @override
  Future<void> saveScoreStopwatchState(ActiveScoreStopwatchState state) async {}

  @override
  Future<void> saveSetTimerState(ActiveSetTimerState state) async {}

  @override
  Future<void> saveExerciseStepProgressState(
    ActiveExerciseStepProgressState state,
  ) async {}

  @override
  Future<void> saveExerciseStepResult(ActiveExerciseStepResult result) async {}

  @override
  Future<void> deleteScoreStopwatchState({
    required String sessionId,
    required int programIndex,
    required int exerciseIndex,
    required int setIndex,
    required DateTime deletedAt,
  }) async {}

  @override
  Future<void> saveSetResult(ActiveSetResult result) async {}
}
