import '../../about/external/data/Program.dart';
import '../../about/external/data/SessionLog.dart';
import '../../about/external/data/SessionTemplate.dart';
import '../../about/external/data/pages/request/HistoryRequest.dart';
import '../../about/external/data/pages/request/PageAndSort.dart';
import '../../about/external/data/pages/request/Pageable.dart';
import '../../about/external/data/pages/request/Sort.dart';
import '../../about/external/data/pages/response/SessionLogPage.dart';
import '../../about/external/initial/IdRequest.dart';
import '../../utils/IntegrityEngine.dart';
import '../../utils/ObjectConvertors.dart';
import '../parent/ParentViewModel.dart';
import 'ConnectTraining.dart';

class ViewTraining extends ParentViewModel {
  ConnectTraining connection;

  ViewTraining(super.context, this.connection);

  void loadProgram() async {
    if (!await hasNetwork(() => loadProgram())) return;

    showLoading("Loading your program");

    try {
      final response = await getDataManager().getMyPrograms(HistoryRequest(
        query: PageAndSort(
          sort: Sort('desc', 'active'),
          page: Pageable(0, 0, 20, 0),
        ),
      ));

      final List<Program> programs = getProgramList(response.data);

      final Program active = programs.firstWhere(
        (item) => item.active,
        orElse: () => programs.isEmpty ? Program() : programs.first,
      );

      await getDataManager().setActiveProgram(active);

      final List<SessionTemplate> sessions = await _loadSessions(active);

      closeLoading();

      connection.onProgramLoaded(active, sessions);

      loadHistory(active);
    } catch (e) {
      handleError(e, () => loadProgram(), () => dismissError(), "Retry");
    }
  }

  Future<List<SessionTemplate>> _loadSessions(Program program) async {
    if (program.id == null) {
      return <SessionTemplate>[];
    }

    final response = await getDataManager()
        .getProgramSessions(IdRequest(id: program.id ?? ""));

    return getSessionTemplateList(response.data);
  }

  void loadHistory(Program program) async {
    try {
      final response = await getDataManager().getSessionHistory(HistoryRequest(
        query: PageAndSort(
          sort: Sort('desc', 'startedAt'),
          page: Pageable(0, 0, 20, 0),
        ),
      ));

      final SessionLogPage page = SessionLogPage.fromJson(response.data);

      connection.onHistoryLoaded(page.content);

      _checkLoadCeilings(program, page.content);
    } catch (e) {
      handleError(e, () => loadHistory(program), () => dismissError(), "Retry");
    }
  }

  /// Plyometrics is the one modality where the app should stop you rather than
  /// push you — CNS and connective tissue do not recover on a motivation
  /// schedule.
  void _checkLoadCeilings(Program program, List<SessionLog> history) {
    final DateTime weekStart =
        DateTime.now().subtract(Duration(days: DateTime.now().weekday - 1));

    final List<SessionLog> thisWeek = history
        .where((session) =>
            session.startedAt != null &&
            session.startedAt!.isAfter(weekStart))
        .toList();

    final int contacts = IntegrityEngine.weeklyContacts(thisWeek);

    if (IntegrityEngine.contactCeilingBreached(
        thisWeek, program.weeklyContactCeiling)) {
      connection.onContactCeilingReached(
          contacts, program.weeklyContactCeiling);
      return;
    }

    _checkRecovery(program, history);
  }

  void _checkRecovery(Program program, List<SessionLog> history) {
    // The most recent hard lower-body session gates the next one.
    final List<SessionLog> hard = history
        .where((session) => session.sessionRpe >= 8 && session.startedAt != null)
        .toList();

    if (hard.isEmpty) {
      return;
    }

    final DateTime last = hard.first.startedAt!;

    if (IntegrityEngine.recoveredEnough(last, program.lowerBodyRecoveryHours)) {
      return;
    }

    final int elapsed = DateTime.now().difference(last).inHours;

    connection.onRecoveryBlocked(program.lowerBodyRecoveryHours - elapsed);
  }
}
