import 'ExcuseCluster.dart';

/// The weekly parent-teacher conference. One assigned action for next week —
/// not five.
class ReportCard {
  String? id;

  DateTime? periodStart;

  DateTime? periodEnd;

  /// Completion rate 0..1, keyed by commitment class name.
  Map<String, double> completionByClass;

  /// Completion rate 0..1, keyed by category.
  Map<String, double> completionByCategory;

  /// Completion rate 0..1, keyed by weekday (1 = Monday).
  Map<int, double> completionByWeekday;

  /// The recurring window where things go to die, as an hour of the day.
  int worstHour;

  /// Debt score sampled per day across the period.
  List<double> debtTrend;

  /// The commitments dodged most, highest first.
  List<DeferralCount> deferralLeaderboard;

  List<ExcuseCluster> excuseTaxonomy;

  /// Estimation multiplier per category, surfaced here rather than hidden.
  Map<String, double> estimationAccuracy;

  /// Training adherence 0..1 across the period.
  double trainingAdherence;

  double programIntegrity;

  /// The single assigned action for next week.
  String assignedAction;

  /// Cosmetic but effective.
  String grade;

  /// Rationed, specific praise — empty when nothing was genuinely earned.
  String praise;

  ReportCard({
    this.id,
    this.periodStart,
    this.periodEnd,
    Map<String, double>? completionByClass,
    Map<String, double>? completionByCategory,
    Map<int, double>? completionByWeekday,
    this.worstHour = -1,
    List<double>? debtTrend,
    List<DeferralCount>? deferralLeaderboard,
    List<ExcuseCluster>? excuseTaxonomy,
    Map<String, double>? estimationAccuracy,
    this.trainingAdherence = 0,
    this.programIntegrity = 0,
    this.assignedAction = "",
    this.grade = "",
    this.praise = "",
  })  : completionByClass = completionByClass ?? <String, double>{},
        completionByCategory = completionByCategory ?? <String, double>{},
        completionByWeekday = completionByWeekday ?? <int, double>{},
        debtTrend = debtTrend ?? <double>[],
        deferralLeaderboard = deferralLeaderboard ?? <DeferralCount>[],
        excuseTaxonomy = excuseTaxonomy ?? <ExcuseCluster>[],
        estimationAccuracy = estimationAccuracy ?? <String, double>{};

  factory ReportCard.fromJson(Map<String, dynamic> json) {
    final Map<String, double> byClass = <String, double>{};
    if (json['completionByClass'] != null) {
      (json['completionByClass'] as Map<String, dynamic>).forEach((key, value) {
        byClass[key] = (value ?? 0).toDouble();
      });
    }

    final Map<String, double> byCategory = <String, double>{};
    if (json['completionByCategory'] != null) {
      (json['completionByCategory'] as Map<String, dynamic>)
          .forEach((key, value) {
        byCategory[key] = (value ?? 0).toDouble();
      });
    }

    final Map<int, double> byWeekday = <int, double>{};
    if (json['completionByWeekday'] != null) {
      (json['completionByWeekday'] as Map<String, dynamic>)
          .forEach((key, value) {
        byWeekday[int.tryParse(key) ?? 1] = (value ?? 0).toDouble();
      });
    }

    final Map<String, double> estimation = <String, double>{};
    if (json['estimationAccuracy'] != null) {
      (json['estimationAccuracy'] as Map<String, dynamic>)
          .forEach((key, value) {
        estimation[key] = (value ?? 1).toDouble();
      });
    }

    return ReportCard(
      id: json['id'],
      periodStart: DateTime.tryParse(json['periodStart'] ?? ""),
      periodEnd: DateTime.tryParse(json['periodEnd'] ?? ""),
      completionByClass: byClass,
      completionByCategory: byCategory,
      completionByWeekday: byWeekday,
      worstHour: json['worstHour'] ?? -1,
      debtTrend: json['debtTrend'] == null
          ? <double>[]
          : (json['debtTrend'] as List)
              .map((item) => (item ?? 0).toDouble() as double)
              .toList(),
      deferralLeaderboard: json['deferralLeaderboard'] == null
          ? <DeferralCount>[]
          : (json['deferralLeaderboard'] as List)
              .map((item) => DeferralCount.fromJson(item))
              .toList(),
      excuseTaxonomy: json['excuseTaxonomy'] == null
          ? <ExcuseCluster>[]
          : (json['excuseTaxonomy'] as List)
              .map((item) => ExcuseCluster.fromJson(item))
              .toList(),
      estimationAccuracy: estimation,
      trainingAdherence: (json['trainingAdherence'] ?? 0).toDouble(),
      programIntegrity: (json['programIntegrity'] ?? 0).toDouble(),
      assignedAction: json['assignedAction'] ?? "",
      grade: json['grade'] ?? "",
      praise: json['praise'] ?? "",
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['id'] = id;
    data['periodStart'] = periodStart?.toIso8601String();
    data['periodEnd'] = periodEnd?.toIso8601String();
    data['completionByClass'] = completionByClass;
    data['completionByCategory'] = completionByCategory;
    data['completionByWeekday'] =
        completionByWeekday.map((key, value) => MapEntry(key.toString(), value));
    data['worstHour'] = worstHour;
    data['debtTrend'] = debtTrend;
    data['deferralLeaderboard'] =
        deferralLeaderboard.map((item) => item.toJson()).toList();
    data['excuseTaxonomy'] =
        excuseTaxonomy.map((item) => item.toJson()).toList();
    data['estimationAccuracy'] = estimationAccuracy;
    data['trainingAdherence'] = trainingAdherence;
    data['programIntegrity'] = programIntegrity;
    data['assignedAction'] = assignedAction;
    data['grade'] = grade;
    data['praise'] = praise;
    return data;
  }
}

/// One row of the deferral leaderboard — the tasks dodged most.
class DeferralCount {
  String commitmentId;

  String title;

  int count;

  DeferralCount({
    this.commitmentId = "",
    this.title = "",
    this.count = 0,
  });

  factory DeferralCount.fromJson(Map<String, dynamic> json) {
    return DeferralCount(
      commitmentId: json['commitmentId'] ?? "",
      title: json['title'] ?? "",
      count: json['count'] ?? 0,
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['commitmentId'] = commitmentId;
    data['title'] = title;
    data['count'] = count;
    return data;
  }
}
