import 'dart:async';

import 'package:fis_common/logger/logger.dart';
import 'package:fis_jsonrpc/rpc.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flyinsono/architecture/router/mobile/names.dart';
import 'package:flyinsono/architecture/router/router.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:flyinsono/rpc.dart';
import 'package:get/get.dart';

import 'index.dart';
import 'models.dart';

class MobileCalendarController extends GetxController {
  MobileCalendarController();

  final state = MobileCalendarState();

  /// 月视图滑动切换控制器
  final _monthViewPageController = PageController();
  PageController get monthViewPageController => _monthViewPageController;

  /// 月视图弹出层列表滑动控制器
  final _monthViewPopupScrollController = ScrollController();
  ScrollController get monthViewPopupScrollController =>
      _monthViewPopupScrollController;

  /// 当前滑动状态
  bool _isSwiping = false;

  /// 月视图日历容器的 key
  final monthViewContainerKey = GlobalKey();

  /// 月视图日历容器每一行的高度[只初始化一次，无需响应式]
  double _monthViewContainerRowHeight = 0;
  double get monthViewContainerRowHeight => _monthViewContainerRowHeight;

  /// 月视图日历容器一半的高度[只初始化一次，无需响应式]
  double _monthViewContainerHalfHeight = 0;
  double get monthViewContainerHalfHeight => _monthViewContainerHalfHeight;

  /// 月视图切换监听器
  bool monthViewPageListener(notification) {
    if (notification is OverscrollIndicatorNotification) {
      if (state.isShowMonthViewDetailPopup) return true;
      if (notification.leading) {
        _onSwipedToLeftEnd();
      } else {
        _onSwipedToRightEnd();
      }
    }
    if (notification is UserScrollNotification) {
      /// 用户滑动时
      if (notification.direction != ScrollDirection.idle) {
        if (state.isShowMonthViewDetailPopup) return true;
        _isSwiping = true;
        state.isPageChanging = true;
        closeMonthViewPopup();

        /// 设置42个空数组
        state.monthViewScheduleList = List.generate(42, (index) => []);
      }
    }
    return true;
  }

  /// 滑动结束定时器
  Timer _swipeEndTimer = Timer(Duration(milliseconds: 0), () {});

  /// 月视图手势监听器
  void onMonthViewPagePointerUp(PointerUpEvent event) {
    if (!_isSwiping) return;
    _isSwiping = false;
    if (_swipeEndTimer.isActive) {
      _swipeEndTimer.cancel();
    }
    _swipeEndTimer = Timer(
      Duration(milliseconds: 400),
      () {
        if (!_isSwiping) {
          state.isPageChanging = false;
          _updateScheduleByDaysList(CalendarUtils.get42DaysStartEndByDateTime(
              state.selectedDate4MonthView));
        }
      },
    );
  }

  /// 月视图切换事件
  void onMonthViewPageChanged(int index) {
    HapticFeedback.mediumImpact();
    // print("当前实际页面为：${index + state.realPageIndex - state.centerPageIndex}");
    state.selectedDate4MonthView = CalendarUtils.getMonthAfter(
      state.currDateTime,
      index + state.realPageIndex - state.centerPageIndex,
    );
  }

  /// TODO:[Gavin] 打开侧面菜单
  void openMenu() {
    Get.snackbar(
      "前方正在施工",
      "TODO 打开菜单",
    );
  }

  /// 打开月视图详情弹窗
  void openMonthViewPopup(int indexInPage, DateTime date) {
    state.selectedIndex4MonthPopup = indexInPage;
    state.selectedDateTime4MonthPopup = date;
    state.isShowMonthViewDetailPopup = true;
    state.monthViewPopupScheduleList = state.monthViewScheduleList[indexInPage];
  }

  /// 关闭月视图详情弹窗
  void closeMonthViewPopup() {
    state.isShowMonthViewDetailPopup = false;
    monthViewPopupScrollController.jumpTo(0);
  }

  /// 点击进入会诊详情页
  void openConsultationDetail(String consultationCode) async {
    try {
      await router.to(MobileRouteNames.RealTimeRemedical.ConsultationDetail,
          parameters: {
            'consultationCode': consultationCode,
          });
    } catch (e) {
      logger.e("openEditInit exception:", e);
      printError(info: e.toString());
    }
  }

  /// 在 widget 内存中分配后立即调用。
  @override
  void onInit() {
    super.onInit();
    final ScheduleTypeConfig scheduleTypeConfig = ScheduleTypeConfig();
    state.scheduleTypeList = scheduleTypeConfig.scheduleTypeList;
  }

  /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
  @override
  void onReady() {
    _initLayout();
    monthViewPageController.jumpToPage(state.centerPageIndex);
    _updateMonthIndexList();
    _updateScheduleByDaysList(CalendarUtils.get42DaysStartEndByDateTime(
        state.selectedDate4MonthView));
    super.onReady();
  }

  /// 在 [onDelete] 方法之前调用。
  @override
  void onClose() {
    super.onClose();
  }

  /// dispose 释放内存
  @override
  void dispose() {
    _swipeEndTimer.cancel();
    super.dispose();
  }

  /// 已滑动至左侧边界
  void _onSwipedToLeftEnd() {
    print("已滑动至左侧边界");
    state.realPageIndex = state.realPageIndex - state.centerPageIndex;
    monthViewPageController.jumpToPage(state.centerPageIndex);
    _updateMonthIndexList();
  }

  /// 已滑动至右侧边界
  void _onSwipedToRightEnd() {
    print("已滑动至右侧边界");
    state.realPageIndex = state.realPageIndex + state.centerPageIndex;
    monthViewPageController.jumpToPage(state.centerPageIndex);
    _updateMonthIndexList();
  }

  /// 更新下标数组
  void _updateMonthIndexList() {
    /// 月视图下标数组
    state.monthIndexList = List.generate(
      state.monthViewPageCount,
      (index) => index + state.realPageIndex - state.centerPageIndex,
    );
  }

  /// 通过 [daysList] 获取日程列表
  ///
  /// [daysList] 日期列表
  ///
  /// 给 [monthViewScheduleList] 赋值更新
  /// TOTO:[Gavin]做数据缓存，避免重复多次请求
  Future<void> _updateScheduleByDaysList(List<DateTime> daysStartEnd) async {
    final List<ClientScheduleDTO> schedlueList =
        await rpc.user.findSchedulesAsync(
      FindSchedulesRequest(
        token: Store.user.token,
        startTime: daysStartEnd.first.toUtc(),
        endTime: daysStartEnd.last.toUtc(),
      ),
    );

    /// 视图中42个日期的日程列表
    List<List<MonthViewScheduleModel>> newViewScheduleList = [];

    /// 获取到的所有日程数据
    List<MonthViewScheduleModel> newScheduleDataList = [];
    for (final ClientScheduleDTO schedule in schedlueList) {
      final MonthViewScheduleModel newSchedule = MonthViewScheduleModel(
        startTime: (schedule.startTime ?? DateTime.now()).toLocal(),
        endTime: (schedule.endTime ?? DateTime.now()).toLocal(),
        type: state.scheduleTypeList.firstWhere(
          (element) => element.typeEnum == schedule.status,
        ),
        title: schedule.title ?? "",
        id: schedule.relevanceCode ?? "",
      );
      newScheduleDataList.add(newSchedule);
    }

    /// 将日程数据按日期分组 42个日期，第一天 daysStartEnd.first
    for (int i = 0; i < 42; i++) {
      final DateTime currDate = daysStartEnd.first.add(Duration(days: i));
      final List<MonthViewScheduleModel> currDateScheduleList =
          newScheduleDataList
              .where(
                (element) => CalendarUtils.isSameDay(
                  element.startTime,
                  currDate,
                ),
              )
              .toList();

      /// 将 currDateScheduleList 按时间排序
      currDateScheduleList.sort((a, b) => a.startTime.compareTo(b.startTime));
      newViewScheduleList.add(currDateScheduleList);
    }
    state.monthViewScheduleList = newViewScheduleList.toSet().toList();
  }

  /// 布局初始化
  void _initLayout() {
    final gridAreaHeight = _getGridAreaHeight(monthViewContainerKey);
    _setMaxScheduleLines(gridAreaHeight);
    _monthViewContainerRowHeight = gridAreaHeight / 6;
    _monthViewContainerHalfHeight = gridAreaHeight / 2;
  }

  /// 计算日历格内的最大日程行数
  void _setMaxScheduleLines(double gridAreaHeight) {
    final rowHeight = gridAreaHeight / 6;
    final scheduleAreaHeight = rowHeight - 40; // 40 是日期的高度
    final maxLines = (scheduleAreaHeight / 16).floor(); // 16 是每个日程的高度

    /// 此处的减一行可以给日程的最后一行留出空间 [大屏手机]
    state.monthViewScheduleMaxLines = maxLines > 4 ? maxLines - 1 : maxLines;
  }

  /// 通过组件的 key 获取对应的组件的大小信息
  double _getGridAreaHeight(GlobalKey key) {
    try {
      final RenderBox renderBox =
          key.currentContext?.findRenderObject() as RenderBox;
      return renderBox.size.height;
    } catch (e) {
      // print('fail to count the size: $e');
      return 0;
    }
  }
}
