import 'package:fis_common/event/event_type.dart';
import 'package:flyinsono/architecture/states/states.dart';
import 'package:flyinsono/pages/schedule/calendar/models.dart';
import 'package:flyinsono/pages/schedule/calendar/utils/calendar_utils.dart';
import 'package:fis_jsonrpc/rpc.dart';
import 'package:flyinsono/rpc.dart';
import 'package:get/get.dart';

class CalendarController extends GetxController {
  CalendarController();

  DateTime currSelectedDate = DateTime.now(); // 当前选中的日期
  int selectedDayIndex = 0; // 当前选择的日期的下标，从0开始，范围是0-34 有时是 0-41
  int maxDaysLength = 35; // 最大的日期数组长度，有时是42
  List<ScheduleModel> currScheduleList = []; // 当前维护的日程列表【数据源】
  List<ScheduleType> scheduleTypeList = []; // 当前维护的一个日程类型列表
  List<String> needDisplayTypeNameList = []; // 维护一个需要展示的日程类型列表
  List<MiniViewDayModel> miniViewDaysList = []; // 当前维护的迷你日历列表

  /// 日程列表与日期列表拼装，即可变成月视图大列表
  List<MonthViewDayModel> get monthViewDaysList {
    final List<MonthViewDayModel> monthViewDaysList = [];
    for (int i = 0; i < maxDaysLength; i++) {
      final MiniViewDayModel miniViewDayStructure = miniViewDaysList[i];
      final MonthViewDayModel monthViewDayStructure = MonthViewDayModel(
        index: miniViewDayStructure.index,
        date: miniViewDayStructure.date,
        isToday: miniViewDayStructure.isToday,
        isCurrentMonth: miniViewDayStructure.isCurrentMonth,
        isSelected: miniViewDayStructure.isSelected,
        scheduleList: [],
      );
      monthViewDaysList.add(monthViewDayStructure);
    }
    final List<ScheduleModel> sortedScheduleList = currScheduleList
      ..sort((ScheduleModel a, ScheduleModel b) =>
          a.startTime!.compareTo(b.startTime!));
    for (final ScheduleModel schedule in sortedScheduleList) {
      final DateTime scheduleDate = schedule.day;
      // final int scheduleDay = scheduleDate.day;
      for (int i = 0; i < maxDaysLength; i++) {
        final MiniViewDayModel miniViewDayStructure = miniViewDaysList[i];
        final MonthViewDayModel monthViewDayStructure = monthViewDaysList[i];
        if (miniViewDayStructure.isCurrentMonth &&
            miniViewDayStructure.date == scheduleDate) {
          monthViewDayStructure.scheduleList.add(schedule);
        }
      }
    }
    return monthViewDaysList;
  }

  /// 当前选中的年月字符串
  String get currentYearMonth =>
      CalendarUtils.formatYearMonth(currSelectedDate);

  /// 当前选中的是否是今天
  bool get isToday => miniViewDaysList[selectedDayIndex].isToday;

  /// 月视图日历行数
  int get calendarLines => maxDaysLength ~/ 7;

  /// 数据变更事件通知
  FEventHandler<void> onDaysListChange = FEventHandler<void>();

  @override
  void onInit() {
    super.onInit();
    _initScheduleType();
    currSelectedDate = DateTime.now();
    miniViewDaysList = _getMiniViewDaysListByMonth(currSelectedDate);
    _updateScheduleByDaysList(miniViewDaysList);
    selectToday();
    updateScheduleTypeList();
    _setMiniCalendarSchedule(currScheduleList);
  }

  /// 初始化日程类型，填充到 scheduleTypeList
  void _initScheduleType() {
    final ScheduleTypeConfig scheduleTypeConfig = ScheduleTypeConfig();
    scheduleTypeList = scheduleTypeConfig.scheduleTypeList;
  }

  /// 选中今天
  Future<void> selectToday() async {
    /// 如果当前不是当月，则切换到当月
    final DateTime now = DateTime.now();
    if (!CalendarUtils.isSameMonth(now, currSelectedDate)) {
      currSelectedDate = now;
      miniViewDaysList = _getMiniViewDaysListByMonth(currSelectedDate);
      await _updateScheduleByDaysList(miniViewDaysList);
    }
    for (var element in miniViewDaysList) {
      if (element.isToday) {
        handleSelectedDayByIndex(element.index);
        break;
      }
    }
  }

  /// 切换到上个月 (keepSelectedDay 是否保持选中的日期)
  Future<void> handleLastMonth([bool keepSelectedDay = false]) async {
    final currDay = miniViewDaysList[selectedDayIndex].date.day;
    currSelectedDate = CalendarUtils.getPreviousMonth(currSelectedDate);
    miniViewDaysList = _getMiniViewDaysListByMonth(currSelectedDate);
    await _updateScheduleByDaysList(miniViewDaysList);
    if (keepSelectedDay) {
      _selectDay(currDay, miniViewDaysList);
    } else {
      _selectFirstDay();
    }
    onDaysListChange.emit(this, null);
  }

  /// 切换到下个月 (keepSelectedDay 是否保持选中的日期)
  Future<void> handleNextMonth([bool keepSelectedDay = false]) async {
    final currDay = miniViewDaysList[selectedDayIndex].date.day;
    currSelectedDate = CalendarUtils.getNextMonth(currSelectedDate);
    miniViewDaysList = _getMiniViewDaysListByMonth(currSelectedDate);
    await _updateScheduleByDaysList(miniViewDaysList);
    if (keepSelectedDay) {
      _selectDay(currDay, miniViewDaysList);
    } else {
      _selectFirstDay();
    }
    onDaysListChange.emit(this, null);
  }

  /// 通过下标选中日期，下标从0开始，允许的范围是0-34，有时是 0-42
  void handleSelectedDayByIndex(int index) {
    assert(index >= 0 && index < maxDaysLength,
        'index must be in 0-$maxDaysLength');
    assert(miniViewDaysList.length == maxDaysLength,
        'miniViewDaysList length must be maxDaysLength');
    for (var element in miniViewDaysList) {
      if (element.index == index) {
        element.isSelected = true;
      } else {
        element.isSelected = false;
      }
    }
    selectedDayIndex = index;
    onDaysListChange.emit(this, null);
  }

  /// 日程数据筛选器【仅筛选出需要显示的日程】
  List<ScheduleModel> scheduleListFilter(List<ScheduleModel> scheduleList) {
    final List<ScheduleModel> result = [];
    for (ScheduleModel element in scheduleList) {
      if (_isDisplayScheduleType(element.type.typeName)) {
        result.add(element);
      }
    }
    return result;
  }

  /// 更新日程列表显示情况（迷你日历的小点）
  ///
  /// 只接收状态更新到 [needDisplayTypeNameList] 不修改 [scheduleTypeList]
  void updateScheduleTypeList() {
    needDisplayTypeNameList = [];
    for (var element in scheduleTypeList) {
      if (element.isSelected) {
        needDisplayTypeNameList.add(element.typeName);
      }
    }
    _setMiniCalendarSchedule(currScheduleList);
    // 通知更新
    onDaysListChange.emit(this, null);
  }

  /// 通过 [daysList] 获取日程列表
  ///
  /// [daysList] 日期列表
  ///
  /// 给 [currScheduleList] 赋值更新
  Future<void> _updateScheduleByDaysList(
      List<MiniViewDayModel> daysList) async {
    final List<ClientScheduleDTO> schedlueList =
        await rpc.user.findSchedulesAsync(
      FindSchedulesRequest(
        token: Store.user.token,
        startTime: daysList[0].date.toUtc(),
        endTime: daysList[maxDaysLength - 1].date.toUtc(),
      ),
    );
    currScheduleList.clear();
    final supportedScheduleTypeList =
        scheduleTypeList.map((e) => e.typeEnum).toList();
    for (final ClientScheduleDTO schedule in schedlueList) {
      if (supportedScheduleTypeList.contains(schedule.status)) {
        final ScheduleModel newSchedule = ScheduleModel(
          day: CalendarUtils.getDay(
              (schedule.startTime ?? schedule.endTime ?? DateTime.now())
                  .toLocal()),
          startTime: schedule.startTime?.toLocal(),
          endTime: schedule.endTime?.toLocal(),
          type: scheduleTypeList.firstWhere(
            (element) => element.typeEnum == schedule.status,
          ),
          title: schedule.title ?? "",
          content: schedule.relevanceCode ?? "",
        );
        currScheduleList.add(newSchedule);
      }
    }
    _setMiniCalendarSchedule(currScheduleList);

    onDaysListChange.emit(this, null);
  }

  /// 根据日期所在的月来获取迷你视图日期列表
  List<MiniViewDayModel> _getMiniViewDaysListByMonth(DateTime day) {
    final year = day.year;
    final month = day.month;
    final today = CalendarUtils.getDay(DateTime.now());
    final daysList = CalendarUtils.getDaysListByMonth(year, month);
    final miniViewDaysList = <MiniViewDayModel>[];

    /// 更新最长尺寸
    maxDaysLength = daysList.length;

    for (int i = 0; i < daysList.length; i++) {
      miniViewDaysList.add(MiniViewDayModel(
        index: i,
        date: daysList[i],
        isToday: daysList[i] == today,
        isCurrentMonth: daysList[i].month == month,
      ));
    }
    return miniViewDaysList;
  }

  /// 选中当月第一天
  void _selectFirstDay() {
    for (var element in miniViewDaysList) {
      if (element.isCurrentMonth) {
        handleSelectedDayByIndex(element.index);
        break;
      }
    }
  }

  /// 根据日期选中指定的一天
  void _selectDay(int day, List<MiniViewDayModel> newList) {
    for (var element in newList) {
      if (element.date.day == day && element.isCurrentMonth) {
        handleSelectedDayByIndex(element.index);
        return;
      }
    }
    int nextSearchDay = day - 1;
    while (nextSearchDay > 0) {
      for (var element in newList) {
        if (element.date.day == nextSearchDay && element.isCurrentMonth) {
          handleSelectedDayByIndex(element.index);
          return;
        }
      }
      nextSearchDay--;
    }
  }

  /// 给每个日期设置是否存在日程
  void _setMiniCalendarSchedule(List<ScheduleModel> scheduleList) {
    /// 如果 scheduleList 中存在 daysList 中的日期，则设置 hasSchedule 为 true
    for (var day in miniViewDaysList) {
      day.hasSchedule = false;
      for (var element in scheduleList) {
        var existSchedule = false;
        if (element.day.year == day.date.year &&
            element.day.month == day.date.month &&
            element.day.day == day.date.day) {
          if (_isDisplayScheduleType(element.type.typeName)) {
            existSchedule = true;
          }
        }
        if (existSchedule) {
          day.hasSchedule = true;
          break;
        }
      }
    }
  }

  /// 是否为需要显示的日程
  bool _isDisplayScheduleType(String typeName) {
    if (needDisplayTypeNameList.contains(typeName)) {
      return true;
    }
    return false;
  }
}
