import 'dart:convert';
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map/src/consts/map_constants.dart';
import 'package:flutter_map/src/entity/camera_position.dart';
import 'package:flutter_map/src/entity/codable.dart';
import 'package:flutter_map/src/entity/lat_lng.dart';
import 'package:flutter_map/src/entity/marker.dart';
import 'package:flutter_map/src/entity/polyline.dart';
import 'package:flutter_map/src/interface/map_fun_interface.dart';
import 'package:flutter_map/src/interface/map_marker_interface.dart';
import 'package:flutter_map/src/interface/map_polyline_interface.dart';
import '../entity/map_padding.dart';

class MapController
    implements MapMarkerInterface, MapFunInterface, MapPolylineInterface {
  final _pendingOperations = <Map<String, dynamic>>[];
  MethodChannel? _channel;
  bool _isDisposed = false;

  void setChannel(MethodChannel channel) {
    if (_isDisposed) return;

    _channel = channel;
    for (final op in _pendingOperations) {
      _executeMethod(op);
    }
    _pendingOperations.clear();
  }

  void _executeMethod(Map<String, dynamic> op) {
    _channel?.invokeMethod(op['method'], op['args']).catchError((e) {
      debugPrint('Method ${op['method']} failed: $e');
    });
  }

  void dispose() {
    _isDisposed = true;
    _pendingOperations.clear();
    _channel?.setMethodCallHandler(null);
    _channel = null;
  }

  @override
  void updateOrAddMarkers(List<Marker> markers) {
    if (_isDisposed || markers.isEmpty) return;
    final serialized = _encodeJson(markers);
    final params = {
      'method': MapConstants.methodUpdateOrAddMarkers,
      'args': serialized
    };
    debugPrint("updateOrAddMarkers...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void moveCamera(CameraPosition cameraPosition) {
    if (_isDisposed) return;
    final serialized = cameraPosition.toJson();
    final params = {
      'method': MapConstants.methodMapMoveCamera,
      'args': serialized
    };
    debugPrint("moveCamera...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void animateCamera(CameraPosition cameraPosition) {
    if (_isDisposed) return;
    final serialized = cameraPosition.toJson();
    final params = {
      'method': MapConstants.methodMapAnimateCamera,
      'args': serialized
    };
    debugPrint("animateCamera...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void updateOrAddMarker(Marker marker) {
    updateOrAddMarkers([marker]);
  }

  @override
  void replaceAllMarkers(List<Marker> markers) {
    if (_isDisposed) return;
    final serialized = _encodeJson(markers);
    final params = {
      'method': MapConstants.methodReplaceAllMarkers,
      'args': serialized
    };
    debugPrint("replaceAllMarkers...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void clearAllMarkers() {
    replaceAllMarkers([]);
  }

  @override
  void addPolyline(Polyline polyline,
      {bool isAnimateCamera = true, MapPadding? mapPadding}) {
    if (_isDisposed || polyline.points.isEmpty) return;
    Map<String, dynamic> map = {};
    map['lineId'] = polyline.lineId;
    map['points'] = _encodeJson(polyline.points);
    map['isAnimateCamera'] = isAnimateCamera;
    map['lineType'] = polyline.lineType.value;
    if (polyline.color != null) {
      map['color'] = polyline.color;
    }
    if (polyline.width != null) {
      map['width'] = polyline.width;
    }
    if (mapPadding != null) {
      map['mapPadding'] = _encodeJson(mapPadding);
    }
    final params = {'method': MapConstants.methodAddPolyline, 'args': map};
    debugPrint("addPolyline...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void clear() {
    if (_isDisposed) return;
    clearAllMarkers();
    final params = {'method': MapConstants.methodMapClear, 'args': ''};
    debugPrint("clear...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void moveToSuitableLocation(List<LatLng> points, {MapPadding? mapPadding}) {
    if (_isDisposed || points.isEmpty) return;
    Map<String, dynamic> map = {};
    map['points'] = _encodeJson(points);
    if (mapPadding != null) {
      map['mapPadding'] = _encodeJson(mapPadding);
    }
    final params = {
      'method': MapConstants.methodMoveToSuitableLocation,
      'args': map
    };
    debugPrint("moveToSuitableLocation...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void removeMarker(String markerId) {
    if (_isDisposed) return;
    final params = {
      'method': MapConstants.methodMarkerRemoveMarker,
      'args': {'markerId': markerId}
    };
    debugPrint("removeMarker...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void removePolyline(String lineId) {
    if (_isDisposed) return;
    final params = {
      'method': MapConstants.methodRemovePolyline,
      'args': {'lineId': lineId}
    };
    debugPrint("removePolyline...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void clearAllPolylines() {
    if (_isDisposed) return;
    final params = {'method': MapConstants.methodClearAllPolylines, 'args': ''};
    debugPrint("clearAllPolylines...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void setMapInteractionEnabled(bool enabled) {
    if (_isDisposed) return;
    final params = {
      'method': MapConstants.methodInteractionEnabled,
      'args': {'enabled': enabled}
    };
    debugPrint("setMapInteractionEnabled...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  @override
  void setMapLogoVisible(bool visible) {
    if (_isDisposed) return;
    final params = {
      'method': MapConstants.methodMapLogoVisible,
      'args': {'visible': visible}
    };
    debugPrint("setMapLogoVisible...params==>$params");
    if (_channel != null) {
      _executeMethod(params);
    } else {
      _pendingOperations.add(params);
    }
  }

  /// iOS 需要一个Map 而不是json string
  dynamic _encodeJson(dynamic object) {
    // iOS平台直接返回对象或转换为JSON对象
    if (Platform.isIOS) {
      if (object is List) {
        return object
            .map((item) => item is Codable ? item.toJson() : item)
            .toList();
      }
      return object is Codable ? object.toJson() : object;
    }

    // 其他平台序列化为JSON字符串
    return jsonEncode(object);
  }
}
