import 'dart:convert';
import 'package:dio/dio.dart' as dio;
import 'package:fis_common/http/http_error.dart';
import 'package:fis_jsonrpc/encrpyt.dart';
import 'package:flutter/foundation.dart';
import 'exception.dart';
import 'http_pool.dart';
import 'interceptor.dart';
import 'request.dart';

// ignore: unused_element
const _C_DIO_FUTURE_ERR_MSG = "Future already completed";
const _C_DIO_FUTURE_ERR_KEYWORDS = "browser_adapter";

/// JSON-RPC Client 基类
class JsonRpcClientBase {
  /// 默认超时时间
  // ignore: non_constant_identifier_names
  static int DEAULT_TIMEOUT = 15000;

  /// 请求加密配置
  static JsonRpcEncryptConfig requestEncryptConfig = JsonRpcEncryptConfig();

  /// 响应加密配置
  static JsonRpcEncryptConfig responseEncryptConfig = JsonRpcEncryptConfig();

  late int _timeout;

  late String _serviceName;

  /// 服务主机地址
  final String host;

  /// 自定义Http header
  final Map<String, String>? headers;

  /// 超时时间(ms)
  int get timeout => _timeout;

  /// 服务名称
  String get serviceName => _serviceName;

  /// 服务请求地址
  String get servicePath =>
      host.endsWith('/') ? "$host$serviceName" : "$host/$serviceName";

  JsonRpcClientBase(
    this.host,
    String serviceName, {
    this.headers,
    int? timeout,
  }) {
    _timeout = timeout ?? DEAULT_TIMEOUT;
    _serviceName = serviceName;
  }

  void setTimeout(int milliseconds) {}

  /// 设置服务名
  void setServiceName(String name) => _serviceName = name;

  /// 请求RPC method
  @protected
  Future<dynamic> call(String method, [args]) async {
    var request = JsonRpcRequest(method, args);
    return _transmit(request);
  }

  /// 通知 RPC method
  @protected
  void notify(String method, [args]) {
    var request = JsonRpcRequest(method, args, notify: true);
    _transmit(request);
  }

  Future<dynamic> _transmit(JsonRpcRequest request) async {
    var result;
    try {
      final req = await jsonRpcInterceptHost.onRequestTransmit(request);

      String package = jsonEncode(req.toJson());

      if (requestEncryptConfig.enable) {
        package = _encryptRequestPackage(package);
      }

      var response = await _postRequest(package);
      if (response == null) throw JsonRpcException(message: "Response Empty");

      result = await _handleDecoded(response);
    } catch (e) {
      if (e is FHttpError)
        await _handleHttpRequestError(JsonRpcNetworkException(e.toString()));
      throw JsonRpcException(message: "Http error", data: e);
    }
    return result;
  }

  Future<dynamic> _postRequest(String package) async {
    try {
      final httpClient = jrpcHttpPool.getClient(
        this.host,
        timeout: this.timeout,
        headers: this.headers,
      );

      var response =
          await httpClient.post('/${this.serviceName}', data: package);
      if (response.statusCode != 200) {
        throw JsonRpcException(
            message: "Http error.Status coder: ${response.statusCode}.");
      }
      if (response.headers.containsKey("response_encrypt_mode")) {
        final resEncryptMode = response.headers["response_encrypt_mode"]!;
        // 解密响应包
        if (resEncryptMode.isNotEmpty && resEncryptMode.first.isNotEmpty) {
          final resPackage = _decryptResponsePackage(response.data);
          final resJsonMap = jsonDecode(resPackage);
          return resJsonMap;
        }
      }
      return response.data;
    } on dio.DioError catch (e) {
      throw JsonRpcException(message: "Http error", data: e);
    } on StateError catch (e) {
      if (e.message == _C_DIO_FUTURE_ERR_MSG) {
        if (e.stackTrace != null &&
            e.stackTrace.toString().contains(_C_DIO_FUTURE_ERR_KEYWORDS)) {
          // don't rethrow
        }
      }
    }
  }

  Future<dynamic> _handleHttpRequestError(JsonRpcNetworkException error) async {
    final res = await jsonRpcInterceptHost.onHttpRequestError(error);

    throw res;
  }

  Future<dynamic> _handleDecoded(Map<String, dynamic> response) async {
    final res = await jsonRpcInterceptHost.onResponse(response);
    if (res.containsKey('error')) {
      var error = JsonRpcServerError.fromJson(res['error']);
      error = await jsonRpcInterceptHost.onResponseError(error);
      throw error;
    }
    var result = res['result'];
    result = await jsonRpcInterceptHost.onResponseResult(result);
    return result;
  }

  String _encryptRequestPackage(String package) {
    final mode = requestEncryptConfig.encryptMode.toLowerCase();
    if (kIsWeb) {
      return package;
    }
    if (mode == "sm2") {
      // 使用国密2加密请求
      return JsonRpcEncryptUtils.sm2Encrypt(
        package,
        requestEncryptConfig.encryptKey,
      );
    }
    if (mode == "rsa") {
      // 使用RSA加密请求
      return JsonRpcEncryptUtils.rsaEncrypt(package);
    }
    return package;
  }

  String _decryptResponsePackage(String package) {
    final mode = responseEncryptConfig.encryptMode.toLowerCase();
    if (mode == "sm4") {
      // 使用国密4解密
      return JsonRpcEncryptUtils.sm4Decrypt(
        package,
        responseEncryptConfig.encryptKey,
      );
    }
    if (mode == "des") {
      // 使用DES解密

      // 截取前8位
      final key = responseEncryptConfig.encryptKey.substring(0, 8);
      return JsonRpcEncryptUtils.desDecrypt(package, key);
    }
    return package;
  }
}

abstract class JsonRpcRequestModelBase {
  /// 转成Json Map
  Map<String, dynamic> toJson();
}