import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:platform/platform.dart';

typedef Future<dynamic> EventHandler(Map<String, dynamic> event);

class JPush {
  final String flutter_log = "| JPUSH | Flutter | ";
  factory JPush() => _instance;

  final MethodChannel _channel;
  final Platform _platform;

  @visibleForTesting
  JPush.private(MethodChannel channel, Platform platform)
      : _channel = channel,
        _platform = platform;

  static final JPush _instance =
      new JPush.private(const MethodChannel('jpush'), const LocalPlatform());

  EventHandler _onReceiveNotification;
  EventHandler _onOpenNotification;
  EventHandler _onReceiveMessage;
  EventHandler _onReceiveNotificationAuthorization;

  void setup({
    String appKey,
    bool production,
    String channel = '',
    bool debug = false,
  }) {
    print(flutter_log + "setup:");

    _channel.invokeMethod('setup', {
      'appKey': appKey,
      'channel': channel,
      'production': production,
      'debug': debug
    });
  }

  ///
  /// 初始化 JPush 必须先初始化才能执行其他操作(比如接收事件传递)
  ///
  void addEventHandler({
    EventHandler onReceiveNotification,
    EventHandler onOpenNotification,
    EventHandler onReceiveMessage,
    EventHandler onReceiveNotificationAuthorization,
  }) {
    print(flutter_log + "addEventHandler:");

    _onReceiveNotification = onReceiveNotification;
    _onOpenNotification = onOpenNotification;
    _onReceiveMessage = onReceiveMessage;
    _onReceiveNotificationAuthorization = onReceiveNotificationAuthorization;
    _channel.setMethodCallHandler(_handleMethod);
  }

  Future<Null> _handleMethod(MethodCall call) async {
    print(flutter_log + "_handleMethod:");

    switch (call.method) {
      case "onReceiveNotification":
        return _onReceiveNotification(call.arguments.cast<String, dynamic>());
      case "onOpenNotification":
        return _onOpenNotification(call.arguments.cast<String, dynamic>());
      case "onReceiveMessage":
        return _onReceiveMessage(call.arguments.cast<String, dynamic>());
      case "onReceiveNotificationAuthorization":
        return _onReceiveNotificationAuthorization(call.arguments.cast<String, dynamic>());
      default:
        throw new UnsupportedError("Unrecognized Event");
    }
  }

  ///
  /// iOS Only
  /// 申请推送权限,注意这个方法只会向用户弹出一次推送权限请求(如果用户不同意,之后只能用户到设置页面里面勾选相应权限),需要开发者选择合适的时机调用。
  ///
  void applyPushAuthority(
      [NotificationSettingsIOS iosSettings = const NotificationSettingsIOS()]) {
    print(flutter_log + "applyPushAuthority:");

    if (!_platform.isIOS) {
      return;
    }

    _channel.invokeMethod('applyPushAuthority', iosSettings.toMap());
  }

  ///
  /// 设置 Tag (会覆盖之前设置的 tags)
  ///
  /// @param {Array} params = [String]
  /// @param {Function} success = ({"tags":[String]}) => {  }
  /// @param {Function} fail = ({"errorCode":int}) => {  }
  ///
  Future<Map<dynamic, dynamic>> setTags(List<String> tags) async {
    print(flutter_log + "setTags:");

    final Map<dynamic, dynamic> result =
        await _channel.invokeMethod('setTags', tags);
    return result;
  }

  ///
  /// 清空所有 tags。
  ///
  /// @param {Function} success = ({"tags":[String]}) => {  }
  /// @param {Function} fail = ({"errorCode":int}) => {  }
  ///
  Future<Map<dynamic, dynamic>> cleanTags() async {
    print(flutter_log + "cleanTags:");

    final Map<dynamic, dynamic> result =
        await _channel.invokeMethod('cleanTags');
    return result;
  }

  ///
  /// 在原有 tags 的基础上添加 tags
  ///
  /// @param {Array} tags = [String]
  /// @param {Function} success = ({"tags":[String]}) => {  }
  /// @param {Function} fail = ({"errorCode":int}) => {  }
  ///

  Future<Map<dynamic, dynamic>> addTags(List<String> tags) async {
    print(flutter_log + "addTags:");

    final Map<dynamic, dynamic> result =
        await _channel.invokeMethod('addTags', tags);
    return result;
  }

  ///
  /// 删除指定的 tags
  ///
  /// @param {Array} tags = [String]
  /// @param {Function} success = ({"tags":[String]}) => {  }
  /// @param {Function} fail = ({"errorCode":int}) => {  }
  ///
  Future<Map<dynamic, dynamic>> deleteTags(List<String> tags) async {
    print(flutter_log + "deleteTags:");

    final Map<dynamic, dynamic> result =
        await _channel.invokeMethod('deleteTags', tags);
    return result;
  }

  ///
  /// 获取所有当前绑定的 tags
  ///
  /// @param {Function} success = ({"tags":[String]}) => {  }
  /// @param {Function} fail = ({"errorCode":int}) => {  }
  ///
  Future<Map<dynamic, dynamic>> getAllTags() async {
    print(flutter_log + "getAllTags:");

    final Map<dynamic, dynamic> result =
        await _channel.invokeMethod('getAllTags');
    return result;
  }

  ///
  /// 重置 alias.
  ///
  /// @param {String} alias
  ///
  /// @param {Function} success = ({"alias":String}) => {  }
  /// @param {Function} fail = ({"errorCode":int}) => {  }
  ///
  Future<Map<dynamic, dynamic>> setAlias(String alias) async {
    print(flutter_log + "setAlias:");

    final Map<dynamic, dynamic> result =
        await _channel.invokeMethod('setAlias', alias);
    return result;
  }

  ///
  /// 删除原有 alias
  ///
  /// @param {Function} success = ({"alias":String}) => {  }
  /// @param {Function} fail = ({"errorCode":int}) => {  }
  ///
  Future<Map<dynamic, dynamic>> deleteAlias() async {
    print(flutter_log + "deleteAlias:");

    final Map<dynamic, dynamic> result =
        await _channel.invokeMethod('deleteAlias');
    return result;
  }

  ///
  /// 设置应用 Badge(小红点)
  ///
  /// @param {Int} badge
  ///
  /// 注意:如果是 Android 手机,目前仅支持华为手机
  ///
  Future setBadge(int badge) async {
    print(flutter_log + "setBadge:");

    await _channel.invokeMethod('setBadge', {"badge": badge});
  }

  ///
  /// 停止接收推送,调用该方法后应用将不再受到推送,如果想要重新收到推送可以调用 resumePush。
  ///
  Future stopPush() async {
    print(flutter_log + "stopPush:");

    await _channel.invokeMethod('stopPush');
  }

  ///
  /// 恢复推送功能。
  ///
  Future resumePush() async {
    print(flutter_log + "resumePush:");

    await _channel.invokeMethod('resumePush');
  }

  ///
  /// 清空通知栏上的所有通知。
  ///
  Future clearAllNotifications() async {
    print(flutter_log + "clearAllNotifications:");

    await _channel.invokeMethod('clearAllNotifications');
  }

  ///
  /// 清空通知栏上某个通知
  /// @param notificationId 通知 id,即:LocalNotification id
  ///
  void clearNotification({@required int notificationId}) {
    print(flutter_log + "clearNotification:");
    _channel.invokeListMethod("clearNotification",notificationId);
  }

  ///
  /// iOS Only
  /// 点击推送启动应用的时候原生会将该 notification 缓存起来,该方法用于获取缓存 notification
  /// 注意:notification 可能是 remoteNotification 和 localNotification,两种推送字段不一样。
  /// 如果不是通过点击推送启动应用,比如点击应用 icon 直接启动应用,notification 会返回 @{}。
  /// @param {Function} callback = (Object) => {}
  ///
  Future<Map<dynamic, dynamic>> getLaunchAppNotification() async {
    print(flutter_log + "getLaunchAppNotification:");

    final Map<dynamic, dynamic> result =
        await _channel.invokeMethod('getLaunchAppNotification');
    return result;
  }

  ///
  /// 获取 RegistrationId, JPush 可以通过制定 RegistrationId 来进行推送。
  ///
  /// @param {Function} callback = (String) => {}
  ///
  Future<String> getRegistrationID() async {
    print(flutter_log + "getRegistrationID:");

    final String rid = await _channel.invokeMethod('getRegistrationID');
    return rid;
  }

  ///
  /// 发送本地通知到调度器,指定时间出发该通知。
  /// @param {Notification} notification
  ///
  Future<String> sendLocalNotification(LocalNotification notification) async {
    print(flutter_log + "sendLocalNotification:");

    await _channel.invokeMethod('sendLocalNotification', notification.toMap());

    return notification.toMap().toString();
  }


  /// 调用此 API 检测通知授权状态是否打开
  Future<bool> isNotificationEnabled() async {
    final Map<dynamic, dynamic> result = await _channel.invokeMethod('isNotificationEnabled');
    bool isEnabled = result["isEnabled"];
    return isEnabled;
  }

  /// 调用此 API 跳转至系统设置中应用设置界面
  void openSettingsForNotification()  {
    _channel.invokeMethod('openSettingsForNotification');
  }

}

class NotificationSettingsIOS {
  final bool sound;
  final bool alert;
  final bool badge;

  const NotificationSettingsIOS({
    this.sound = true,
    this.alert = true,
    this.badge = true,
  });

  Map<String, dynamic> toMap() {
    return <String, bool>{'sound': sound, 'alert': alert, 'badge': badge};
  }
}

/// @property {number} [buildId] - 通知样式:1 为基础样式,2 为自定义样式(需先调用 `setStyleCustom` 设置自定义样式)
/// @property {number} [id] - 通知 id, 可用于取消通知
/// @property {string} [title] - 通知标题
/// @property {string} [content] - 通知内容
/// @property {object} [extra] - extra 字段
/// @property {number} [fireTime] - 通知触发时间(毫秒)
/// // iOS Only
/// @property {number} [badge] - 本地推送触发后应用角标值
/// // iOS Only
/// @property {string} [soundName] - 指定推送的音频文件
/// // iOS 10+ Only
/// @property {string} [subtitle] - 子标题
class LocalNotification {
  final int buildId; //?
  final int id;
  final String title;
  final String content;
  final Map<String, String> extra; //?
  final DateTime fireTime;
  final int badge; //?
  final String soundName; //?
  final String subtitle; //?

  const LocalNotification(
      {@required this.id,
      @required this.title,
      @required this.content,
      @required this.fireTime,
      this.buildId,
      this.extra,
      this.badge = 0,
      this.soundName,
      this.subtitle})
      : assert(id != null),
        assert(title != null),
        assert(content != null),
        assert(fireTime != null);

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'id': id,
      'title': title,
      'content': content,
      'fireTime': fireTime.millisecondsSinceEpoch,
      'buildId': buildId,
      'extra': extra,
      'badge': badge,
      'soundName': soundName,
      'subtitle': subtitle
    }..removeWhere((key, value) => value == null);
  }
}