import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cupertino_datetime_picker/flutter_cupertino_datetime_picker.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:injectable/injectable.dart';
import 'package:location/base/base_controller.dart';
import 'package:location/data/bean/location_info.dart';
import 'package:location/data/consts/constants.dart';
import 'package:location/data/consts/error_code.dart';
import 'package:location/data/repositories/account_repository.dart';
import 'package:location/data/repositories/friends_repository.dart';
import 'package:location/data/repositories/track_repository.dart';
import 'package:location/dialog/loading_dialog.dart';
import 'package:location/handler/error_handler.dart';
import 'package:location/module/member/member_page.dart';
import 'package:location/module/track/track_detail/track_detail_page.dart';
import 'package:location/module/track/track_util.dart';
import 'package:location/resource/string.gen.dart';
import 'package:location/utils/atmob_log.dart';
import 'package:location/utils/common_expand.dart';
import 'package:location/utils/http_handler.dart';
import 'package:location/utils/toast_util.dart';
import 'package:sliding_sheet2/sliding_sheet2.dart';
import '../../data/bean/atmob_track_point.dart';
import '../../data/bean/user_info.dart';
import '../../dialog/common_confirm_dialog_impl.dart';
import '../../utils/date_util.dart';
import '../../utils/pair.dart';

@injectable
class TrackController extends BaseController
    with GetSingleTickerProviderStateMixin {
  final int errorQueryOriginalDataEmpty = 10; //查询原始数据集为空
  final int errorQueryOriginalTooFew = 11; //查询原始数据集少于2点

  final Rxn<UserInfo> _userInfo = Rxn<UserInfo>();

  UserInfo? get userInfo => _userInfo.value;

  final MapController mapController = MapController();

  final Rxn<DateTime> _trackStartTime = Rxn<DateTime>();
  final Rxn<DateTime> _trackEndTime = Rxn<DateTime>();

  DateTime? get trackStartTime => _trackStartTime.value;

  DateTime? get trackEndTime => _trackEndTime.value;
  SheetController sheetController = SheetController();

  final Rxn<String> _startAddress = Rxn<String>();
  final Rxn<String> _endAddress = Rxn<String>();

  String? get startAddress => _startAddress.value;

  String? get endAddress => _endAddress.value;

  final Duration maxDuration = Duration(days: 1);
  final String timeFormat = "yyyy年-MM月-dd日 HH时:mm分";
  late TabController tabController;
  final RxInt _currentIndex = 0.obs;

  int get currentIndex => _currentIndex.value;

  final Rxn<LocationInfo> _currentLocation = Rxn<LocationInfo>();

  LocationInfo? get currentLocation => _currentLocation.value;

  List<LatLng>? points;
  List<AtmobTrackPoint>? originPoints;

  final RxBool _isShowTraceDetailBtn = false.obs;

  bool get isShowTraceDetailBtn => _isShowTraceDetailBtn.value;

  final TrackRepository trackRepository;
  final FriendsRepository friendsRepository;
  final AccountRepository accountRepository;

  TrackController(
      this.trackRepository, this.friendsRepository, this.accountRepository);

  @override
  void onInit() {
    final param = Get.arguments;
    if (param is UserInfo) {
      _userInfo.value = param;
    }
    tabController = TabController(
        length: 2, vsync: this, initialIndex: _currentIndex.value);
    tabController.addListener(_handleTabChange);
    _initTime();
    _onCurrentLocationQuery(isShow: false);
  }

  @override
  void onReady() {
    super.onReady();
    sheetController.expand();
  }

  void _handleTabChange() {
    if (tabController.indexIsChanging) return;
    _currentIndex.value = tabController.index;
    if (tabController.index == 0) {
      _showTrack();
    } else if (tabController.index == 1) {
      _showCurrentLocation();
    }
  }

  void _initTime() {
    //开始时间往前推一天
    _trackStartTime.value = DateUtil.getNow(subtract: Duration(days: 1));
    _trackEndTime.value = DateUtil.getNow();
  }

  void back() {
    Get.back();
  }

  void onTrackStartTimeClick(BuildContext context) {
    if (userInfo?.virtual == true && accountRepository.memberIsExpired()) {
      MemberPage.start();
      return;
    }
    DatePicker.showDatePicker(context,
        locale: DateTimePickerLocale.zh_cn,
        initialDateTime: _trackStartTime.value,
        dateFormat: timeFormat, onConfirm: (dateTime, selectedIndex) {
      if (trackEndTime != null &&
          DateUtil.isTimeRangeExceed(dateTime, trackEndTime!, maxDuration)) {
        ToastUtil.show(StringName.trackChooseTimeError);
        _trackEndTime.value = dateTime.add(maxDuration);
      }
      _trackStartTime.value = dateTime;
    });
  }

  void onTrackEndTimeClick(BuildContext context) {
    if (userInfo?.virtual == true && accountRepository.memberIsExpired()) {
      MemberPage.start();
      return;
    }
    DatePicker.showDatePicker(context,
        locale: DateTimePickerLocale.zh_cn,
        initialDateTime: _trackEndTime.value,
        dateFormat: timeFormat, onConfirm: (dateTime, selectedIndex) {
      if (trackStartTime != null &&
          DateUtil.isTimeRangeExceed(trackStartTime!, dateTime, maxDuration)) {
        ToastUtil.show(StringName.trackChooseTimeError);
        _trackStartTime.value = dateTime.subtract(maxDuration);
      }
      _trackEndTime.value = dateTime;
    });
  }

  void onTrackQueryClick() {
    if (userInfo?.virtual == false && accountRepository.memberIsExpired()) {
      MemberPage.start();
      return;
    }
    if (currentIndex == 0) {
      _onTrackQuery();
    } else {
      _onCurrentLocationQuery();
    }
  }

  void _onCurrentLocationQuery({bool isShow = true}) {
    if (userInfo == null) {
      return;
    }
    if (userInfo?.isMine == true) {
      _currentLocation.value =
          accountRepository.mineUserInfo.value.lastLocation.value;
      if (isShow) {
        _showCurrentLocation();
      }
    } else {
      if (isShow) LoadingDialog.show(StringName.trackLoadingTxt);
      friendsRepository
          .getUserInfoFromId(userInfo!.id, isVirtual: userInfo!.virtual)
          .then((location) {
        LoadingDialog.hide();
        _currentLocation.value = userInfo?.lastLocation.value;
        if (isShow) {
          _showCurrentLocation();
        }
      }).catchError((error) {
        debugPrint("error: $error");
        ErrorHandler.toastError(error);
      });
    }
  }

  void _onTrackQuery() {
    if (trackStartTime == null || trackEndTime == null || userInfo == null) {
      return;
    }
    LoadingDialog.show(StringName.trackLoadingTxt);
    _startAddress.value = '';
    _endAddress.value = '';
    originPoints = null;
    points = null;
    Future.value().then((_) {
      if (userInfo?.virtual == true) {
        return trackRepository.queryVirtualTrack();
      } else {
        return trackRepository.queryTrack(
            startTime: trackStartTime?.millisecondsSinceEpoch,
            endTime: trackEndTime?.millisecondsSinceEpoch,
            userId: userInfo?.id);
      }
    }).map((trackResponse) {
      final pointsList = trackResponse.trackPoints;
      if (pointsList == null || pointsList.isEmpty) {
        throw TrackQueryException(errorQueryOriginalDataEmpty);
      }
      if (pointsList.length < 2) {
        throw TrackQueryException(errorQueryOriginalTooFew);
      }
      if (userInfo?.virtual == true) {
        int nowMill = DateUtil.getNow().millisecondsSinceEpoch;
        int firstMill = pointsList.first.time;
        int differ = nowMill - firstMill;
        pointsList.first.time = nowMill;
        for (var element in pointsList) {
          element.time = element.time + differ;
        }
      }
      return pointsList;
    }).then((pointsList) async {
      final list = TrackUtil.points2TraceLocation(pointsList);
      List<LatLng>? convertList;
      try {
        convertList = await FlutterMap.queryProcessedTrace(
            lineID: pointsList.hashCode, locations: list);
      } catch (e) {
        AtmobLog.e("TrackController", "queryProcessedTrace error: $e");
      }
      if (convertList == null || convertList.isEmpty) {
        //轨迹纠偏失败，使用原始数据
        convertList = TrackUtil.traceLocation2LatLng(list);
      }
      return Pair(pointsList, convertList);
    }).then((pair) {
      originPoints = pair.first;
      points = pair.second;
      _showTrack();
      _setStartAndEndAddress(start: pair.first.first, end: pair.first.last);
    }).catchError((error) {
      if (error is TrackQueryException) {
        showQueryErrorDialog();
      } else if (error is ServerErrorException) {
        if (error.code == ErrorCode.noMember) {
          MemberPage.start();
          ToastUtil.show(StringName.memberExpired);
        } else {
          ToastUtil.show(error.message);
        }
      } else {
        ErrorHandler.toastError(error);
      }
    }).whenComplete(() {
      LoadingDialog.hide();
      _isShowTraceDetailBtn.value = true;
    });
  }

  @override
  void onClose() {
    super.onClose();
    tabController.dispose();
  }

  void _setStartAndEndAddress(
      {required AtmobTrackPoint start, required AtmobTrackPoint end}) {
    _startAddress.value = start.addr;
    _endAddress.value = end.addr;
  }

  void _showCurrentLocation() {
    mapController.clear();
    if (currentLocation == null || userInfo == null) {
      return;
    }
    mapController.updateOrAddMarker(Marker(
      id: userInfo!.id,
      markerName: userInfo!.getUserNickName(),
      longitude: userInfo!.lastLocation.value?.longitude,
      latitude: userInfo!.lastLocation.value?.latitude,
      markerType: userInfo!.isMine == true
          ? MarkerType.traceEndMinePoint
          : MarkerType.traceEndFriendPoint,
    ));
    mapController.animateCamera(CameraPosition(
        latitude: currentLocation!.latitude,
        longitude: currentLocation!.longitude,
        zoom: 18));
  }

  void _showTrack() {
    mapController.clear();
    if (points == null || points!.length < 2) {
      return;
    }
    mapController.addPolyline(points!,
        mapPadding:
            MapPadding(left: 50, top: 100, right: 50, bottom: Get.height / 2));
    mapController.updateOrAddMarker(Marker(
        id: Constants.traceStartId,
        markerName: '',
        longitude: points!.first.longitude,
        latitude: points!.first.latitude,
        markerType: MarkerType.traceStartPoint));
    mapController.updateOrAddMarker(Marker(
        id: Constants.traceEndId,
        markerName: userInfo?.getUserNickName() ?? '',
        longitude: points!.last.longitude,
        latitude: points!.last.latitude,
        markerType: userInfo?.isMine == true
            ? MarkerType.traceEndMinePoint
            : MarkerType.traceEndFriendPoint));
    //显示起点标记
    // drawMarker();
    //显示终点标记
  }

  void showQueryErrorDialog() {
    showTraceNoDataDialog(onConfirm: () {});
  }

  void onTraceDetailClick() {
    if (accountRepository.memberIsExpired()) {
      MemberPage.start();
      return;
    }
    if (originPoints == null || originPoints!.length < 2) {
      showTraceNoDataDialog(onConfirm: () {});
      return;
    }
    TrackDetailPage.start(originPoints!);
  }
}

class TrackQueryException implements Exception {
  final int code;

  TrackQueryException(this.code);
}
