import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:flutter/foundation.dart';
import 'package:isekai_wiki/global.dart';
import 'package:path_provider/path_provider.dart';

class HttpResponseException implements Exception {
  int statusCode;
  String? statusText;

  HttpResponseException(this.statusCode, {this.statusText});

  @override
  String toString() {
    return "Http error: $statusCode $statusText";
  }
}

class BaseApi {
  static Dio? _dioInstance;
  static CookieJar? cookieJar;

  static Future<Dio> createClient() async {
    var dio = Dio();

    if (!kIsWeb) {
      // HTTP2
      /*
      dio.httpClientAdapter = Http2Adapter(
        ConnectionManager(
          idleTimeout: 10000,
        ),
      );
      */

      // Cookie
      var tempDir = await getTemporaryDirectory();

      cookieJar = PersistCookieJar(
        storage: FileStorage("${tempDir.path}/cookies"),
      );
      dio.interceptors.add(CookieManager(cookieJar!));

      // 缓存
      final cacheOptions = CacheOptions(
        store: MemCacheStore(),
        policy: CachePolicy.request,
        maxStale: const Duration(days: 7),
        priority: CachePriority.normal,
        cipher: null,
        keyBuilder: CacheOptions.defaultCacheKeyBuilder,
        allowPostMethod: false,
      );
      dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));

      // 自定义Header
      dio.interceptors.add(
        InterceptorsWrapper(onRequest: (options, handler) {
          options.headers["X-IsekaiWikiApp-Version"] =
              Global.packageInfo?.version ?? "unknow";
          options.headers["User-Agent"] = "";
          return handler.next(options);
        }),
      );
    }

    return dio;
  }

  static Future<Dio> getClient() async {
    _dioInstance ??= await createClient();

    return _dioInstance!;
  }

  static Future<void> clearCookie() async {
    await cookieJar?.deleteAll();
  }

  static Future<String> get(Uri uri, {Map<String, dynamic>? search}) async {
    var client = await getClient();
    var res = await client.get<String>(
      uri.toString(),
      queryParameters: search,
      options: Options(responseType: ResponseType.plain),
    );

    if (res.statusCode != null && res.statusCode != 200) {
      throw HttpResponseException(res.statusCode!,
          statusText: res.statusMessage!);
    }

    return res.data ?? "";
  }

  static Future<Map<String, dynamic>> getJson(Uri uri,
      {Map<String, dynamic>? search}) async {
    var resText = await get(uri, search: search);
    var resData = jsonDecode(resText);

    if (resData is Map<String, dynamic>) {
      return resData;
    } else {
      return {};
    }
  }

  static Future<String> post(Uri uri,
      {Map<String, dynamic>? search, dynamic data}) async {
    var client = await getClient();

    String? contentType;
    if (data is Map) {
      contentType = Headers.formUrlEncodedContentType;
    }

    var res = await client.post<String>(
      uri.toString(),
      queryParameters: search,
      data: data,
      options:
          Options(responseType: ResponseType.plain, contentType: contentType),
    );

    if (res.statusCode != null && res.statusCode != 200) {
      throw HttpResponseException(res.statusCode!,
          statusText: res.statusMessage!);
    }

    return res.data ?? "";
  }

  static Future<Map> postJson(Uri uri,
      {Map<String, dynamic>? search, dynamic data}) async {
    var resText = await post(uri, search: search, data: data);
    var resData = jsonDecode(resText);

    if (resData is Map) {
      return resData;
    } else {
      return {};
    }
  }
}