import 'dart:collection';
import 'package:habo/constants.dart';
import 'package:habo/habits/habit.dart';
import 'package:habo/model/habit_data.dart';

class StatisticsData {
  String title;
  int topStreak = 0;
  int actualStreak = 0;
  int checks = 0;
  int fails = 0;
  int skips = 0;
  int progress = 0;
  SplayTreeMap<int, Map<DayType, List<int>>> monthlyTracking;

  StatisticsData({
    this.title = '',
    SplayTreeMap<int, Map<DayType, List<int>>>? monthlyTracking,
  }) : monthlyTracking = monthlyTracking ?? SplayTreeMap<int, Map<DayType, List<int>>>();
}

class OverallStatisticsData {
  int totalChecks = 0;
  int totalFails = 0;
  int totalSkips = 0;
  int totalProgress = 0;
}

class AllStatistics {
  List<StatisticsData> allStatistics = [];
  OverallStatisticsData overallStatistics = OverallStatisticsData();
}

class Statistics {
  static AllStatistics calculateStatistics(List<Habit> habits) {
    final result = AllStatistics();

    for (final habit in habits) {
      final data = StatisticsData(title: habit.habitData.title);
      final events = habit.habitData.events;
      bool usingTwoDayRule = false;

      final dates = events.keys.toList();
      for (int i = 0; i < dates.length; i++) {
        final date = dates[i];
        final event = events[date]!;
        final dayType = event[0] is DayType ? event[0] as DayType : DayType.values[event[0] as int];

        // Check date gap
        if (i > 0) {
          final diff = date.difference(dates[i - 1]).inDays;
          if (diff > 1) {
            data.actualStreak = 0;
            usingTwoDayRule = false;
          }
        }

        final yearMonth = date.year * 100 + date.month;
        data.monthlyTracking[yearMonth] ??= {};
        data.monthlyTracking[yearMonth]![dayType] ??= [];
        data.monthlyTracking[yearMonth]![dayType]!.add(date.day);

        switch (dayType) {
          case DayType.check:
            data.checks++;
            data.actualStreak++;
            if (data.actualStreak > data.topStreak) {
              data.topStreak = data.actualStreak;
            }
            usingTwoDayRule = false;
            break;
          case DayType.progress:
            data.progress++;
            if (habit.habitData.habitType == HabitType.numeric && event.length >= 4) {
              final progressValue = event[2] as double? ?? 0.0;
              final target = event[3] as double? ?? habit.habitData.targetValue;
              if (progressValue >= target) {
                data.actualStreak++;
                if (data.actualStreak > data.topStreak) {
                  data.topStreak = data.actualStreak;
                }
              }
            }
            usingTwoDayRule = false;
            break;
          case DayType.fail:
            data.fails++;
            if (habit.habitData.twoDayRule) {
              if (usingTwoDayRule) {
                data.actualStreak = 0;
              } else {
                usingTwoDayRule = true;
              }
            } else {
              data.actualStreak = 0;
            }
            break;
          case DayType.skip:
            data.skips++;
            if (usingTwoDayRule) {
              data.actualStreak = 0;
            }
            break;
          case DayType.clear:
            break;
        }
      }

      result.allStatistics.add(data);
    }

    // Calculate overall
    for (final stat in result.allStatistics) {
      result.overallStatistics.totalChecks += stat.checks;
      result.overallStatistics.totalFails += stat.fails;
      result.overallStatistics.totalSkips += stat.skips;
      result.overallStatistics.totalProgress += stat.progress;
    }

    return result;
  }
}
