import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:habo/constants.dart';
import 'package:habo/model/category.dart';

class HabitData {
  int? id;
  int position;
  String title;
  bool twoDayRule;
  String cue;
  String routine;
  String reward;
  bool showReward;
  bool advanced;
  bool notification;
  TimeOfDay notTime;
  String sanction;
  bool showSanction;
  String accountant;
  HabitType habitType;
  double targetValue;
  double partialValue;
  String unit;
  bool archived;
  SplayTreeMap<DateTime, List> events;
  List<Category> categories;

  // Runtime computed fields
  int streak;
  bool streakVisible;
  bool orangeStreak;

  HabitData({
    this.id,
    this.position = 0,
    this.title = '',
    this.twoDayRule = false,
    this.cue = '',
    this.routine = '',
    this.reward = '',
    this.showReward = false,
    this.advanced = false,
    this.notification = false,
    this.notTime = const TimeOfDay(hour: 20, minute: 0),
    this.sanction = '',
    this.showSanction = false,
    this.accountant = '',
    this.habitType = HabitType.boolean,
    this.targetValue = 100.0,
    this.partialValue = 10.0,
    this.unit = '',
    this.archived = false,
    SplayTreeMap<DateTime, List>? events,
    this.categories = const [],
    this.streak = 0,
    this.streakVisible = false,
    this.orangeStreak = false,
  }) : events = events ?? SplayTreeMap<DateTime, List>();

  bool isCompletedForDate(DateTime date) {
    final key = DateTime(date.year, date.month, date.day);
    final event = events[key];
    if (event == null) return false;
    final dayType = event[0] as DayType;
    if (dayType == DayType.check) return true;
    if (dayType == DayType.progress && event.length >= 4) {
      final progressValue = event[2] as double? ?? 0.0;
      final target = event[3] as double? ?? targetValue;
      return progressValue >= target;
    }
    return false;
  }

  double getProgressForDate(DateTime date) {
    final key = DateTime(date.year, date.month, date.day);
    final event = events[key];
    if (event == null || event.length < 3) return 0.0;
    return event[2] as double? ?? 0.0;
  }

  double getProgressPercentage(DateTime date) {
    final key = DateTime(date.year, date.month, date.day);
    final event = events[key];
    if (event == null || event.length < 4) return 0.0;
    final progressValue = event[2] as double? ?? 0.0;
    final target = event[3] as double? ?? targetValue;
    if (target == 0) return 0.0;
    return (progressValue / target).clamp(0.0, 1.0);
  }

  Map<String, dynamic> toJson() {
    final eventsJson = <String, dynamic>{};
    for (final entry in events.entries) {
      eventsJson[entry.key.toIso8601String()] = [
        entry.value[0] is DayType ? (entry.value[0] as DayType).index : entry.value[0],
        if (entry.value.length > 1) entry.value[1] else '',
        if (entry.value.length > 2) entry.value[2],
        if (entry.value.length > 3) entry.value[3],
      ];
    }

    return {
      'id': id,
      'position': position,
      'title': title,
      'twoDayRule': twoDayRule,
      'cue': cue,
      'routine': routine,
      'reward': reward,
      'showReward': showReward,
      'advanced': advanced,
      'notification': notification,
      'notTime': {'hour': notTime.hour, 'minute': notTime.minute},
      'sanction': sanction,
      'showSanction': showSanction,
      'accountant': accountant,
      'habitType': habitType.index,
      'targetValue': targetValue,
      'partialValue': partialValue,
      'unit': unit,
      'archived': archived,
      'events': eventsJson,
    };
  }

  factory HabitData.fromJson(Map<String, dynamic> json) {
    final eventsJson = json['events'] as Map<String, dynamic>? ?? {};
    final events = SplayTreeMap<DateTime, List>();
    for (final entry in eventsJson.entries) {
      try {
        final date = DateTime.parse(entry.key);
        final value = entry.value as List;
        final dayType = value[0] is int ? DayType.values[value[0] as int] : value[0];
        final event = [
          dayType,
          if (value.length > 1) value[1] else '',
          if (value.length > 2) value[2] else 0.0,
          if (value.length > 3) value[3] else 0.0,
        ];
        events[date] = event;
      } catch (_) {}
    }

    final notTimeJson = json['notTime'];
    TimeOfDay notTime;
    if (notTimeJson is Map) {
      notTime = TimeOfDay(
        hour: notTimeJson['hour'] as int? ?? 20,
        minute: notTimeJson['minute'] as int? ?? 0,
      );
    } else {
      notTime = const TimeOfDay(hour: 20, minute: 0);
    }

    return HabitData(
      id: json['id'] as int?,
      position: json['position'] as int? ?? 0,
      title: json['title'] as String? ?? '',
      twoDayRule: json['twoDayRule'] as bool? ?? false,
      cue: json['cue'] as String? ?? '',
      routine: json['routine'] as String? ?? '',
      reward: json['reward'] as String? ?? '',
      showReward: json['showReward'] as bool? ?? false,
      advanced: json['advanced'] as bool? ?? false,
      notification: json['notification'] as bool? ?? false,
      notTime: notTime,
      sanction: json['sanction'] as String? ?? '',
      showSanction: json['showSanction'] as bool? ?? false,
      accountant: json['accountant'] as String? ?? '',
      habitType: HabitType.values[json['habitType'] as int? ?? 0],
      targetValue: (json['targetValue'] as num?)?.toDouble() ?? 100.0,
      partialValue: (json['partialValue'] as num?)?.toDouble() ?? 10.0,
      unit: json['unit'] as String? ?? '',
      archived: json['archived'] as bool? ?? false,
      events: events,
    );
  }
}
