import 'dart:async';

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

import 'config/location_option.dart';
import 'models/wayz_geocode_response.dart';
import 'models/wayz_reverse_geocode_response.dart';
import 'wz_location_plugin_platform_interface.dart';
import 'wz_scenario_http_api.dart';

class MethodChannelWzLocationPlugin extends WzLocationPluginPlatform {
  @visibleForTesting
  final methodChannel = const MethodChannel('wz_location_plugin');
  @visibleForTesting
  final eventChannel = const EventChannel('wz_location_plugin_stream');

  String _apiKey = "";

  /// Single broadcast bridge; avoid calling [EventChannel.receiveBroadcastStream] per getter access.
  ///
  /// Values may be null (Android sends a stable key set with nulls); never use [Map.cast] to `Object`.
  static final Stream<Map<String, Object?>> _locationBroadcast =
      const EventChannel('wz_location_plugin_stream')
          .receiveBroadcastStream()
          .asBroadcastStream()
          .map<Map<String, Object?>>((element) {
    final raw = element as Map<Object?, Object?>;
    return Map<String, Object?>.fromEntries(
      raw.entries.map((e) => MapEntry(e.key! as String, e.value)),
    );
  });

  @override
  Stream<Map<String, Object?>> get locationStream => _locationBroadcast;

  @override
  Future<String?> getPlatformVersion() async {
    final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
    return version;
  }

  @override
  Future<void> setApiKey(String apiKey) async {
    _apiKey = apiKey;
    await methodChannel.invokeMethod<void>('setApiKey', {'apiKey': apiKey});
  }

  @override
  Future<void> updatePrivacyShow(bool hasContains, bool hasShow) async {
    await methodChannel.invokeMethod<void>('updatePrivacyShow', {
      'hasContains': hasContains,
      'hasShow': hasShow,
    });
  }

  @override
  Future<void> updatePrivacyAgree(bool hasAgree) async {
    await methodChannel.invokeMethod<void>('updatePrivacyAgree', {'hasAgree': hasAgree});
  }

  @override
  Future<void> setLocationOption(WzLocationOption locationOption) async {
    _apiKey = locationOption.apiKey;
    await methodChannel.invokeMethod<void>('setLocationOption', locationOption.toJson());
  }

  @override
  Future<void> startLocation() async {
    await methodChannel.invokeMethod<void>('startLocation');
  }

  @override
  Future<void> stopLocation() async {
    await methodChannel.invokeMethod<void>('stopLocation');
  }

  @override
  Future<WzGeocodeResponse> geocode({
    required String address,
    String? city,
  }) async {
    final api = WzScenarioHttpApi(accessKey: _apiKey);
    return await api.geocode(
      address: address,
      city: city,
    );
  }

  @override
  Future<WzReverseGeocodeResponse> reverseGeocode(
      {required String asset,
      required double longitude,
      required double latitude,
      String? responseSprf,
      String? spatialReference,
      String? source,
      double? accuracy}) async {
    final api = WzScenarioHttpApi(accessKey: _apiKey);
    return await api.reverseGeocode(
      asset: asset,
      longitude: longitude,
      latitude: latitude,
      responseSprf: responseSprf,
      spatialReference: spatialReference,
      source: source,
      accuracy: accuracy,
    );
  }

  @override
  Future<List<WzGeocodeResponse>> searchPoiByKeyword(
      {required String name,
      String? city,
      String? cityCode,
      required int pageIndex,
      required int pageSize}) async {
    final api = WzScenarioHttpApi(accessKey: _apiKey);
    return await api.searchPoiByKeyword(
        name: name,
        city: city,
        cityCode: cityCode,
        pageIndex: pageIndex,
        pageSize: pageSize
    );
  }

  @override
  Future<List<WzGeocodeResponse>> searchPoiNearby(
      {required String location,
      required int radiusMeters,
      String? keywords,
      String? category,
      String? orderBy,
      required int pageIndex,
      required int pageSize}) async {
    final api = WzScenarioHttpApi(accessKey: _apiKey);
    return await api.searchPoiNearby(
        location: location,
        radiusMeters: radiusMeters,
        keywords: keywords,
        category: category,
        orderby: orderBy,
        pageIndex: pageIndex,
        pageSize: pageSize
    );
  }
}
