import 'dart:convert';
import 'dart:io';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/common.dart';
import 'package:aitrainer_app/util/not_found_exception.dart';
import 'package:flutter/services.dart';
import 'package:aitrainer_app/model/cache.dart';

class APIClient with Common, Logging {
  static final APIClient _singleton = APIClient._internal();
  late bool cert;

  factory APIClient() {
    return _singleton;
  }

  APIClient._internal() {
    cert = false;
  }

  dynamic authenticateUser(String email, String password) async {
    var url = Cache().getBaseUrl() + "authenticate";

    try {
      ByteData data = await rootBundle.load('asset/data/aitrainer_server.crt.pem');
      SecurityContext context = SecurityContext.defaultContext;
      if (cert == false) {
        print("Set CERT $cert");
        context.setTrustedCertificatesBytes(data.buffer.asUint8List(), password: "[xxxx]");
        cert = true;
      }

      HttpClient client = new HttpClient(); //context: context Todo provide the right certificate
      client.badCertificateCallback = ((X509Certificate cert, String host, int port) {
        print("Host: $host Port: $port");
        return true;
      });
      var uri = Uri.parse(url);
      final HttpClientRequest request = await client.postUrl(uri);
      request.headers.set('Content-Type', 'application/json');
      request.headers.set('Authorization', '1');

      final body = '{"username":"$email", "password":"$password"}';
      request.write(body);
      HttpClientResponse result = await request.close();
      client.close();
      if (result.statusCode != 200) {
        trace("authentication response: ${result.statusCode}");
        throw Exception("Network error, try again later!");
      }
      return jsonDecode(await result.transform(utf8.decoder).join());
    } catch (exception) {
      print(exception.toString());
      throw Exception("Network error, try again later!");
    }
  }

  Future<String> post(String endPoint, String body) async {
    final url = Cache().getBaseUrl() + endPoint;
    trace(" ------------ http/post body $body - url: $url ");
    try {
      String authToken = Cache().getAuthToken();
      if (authToken.length == 0) {
        var responseJson = await this.authenticateUser(Cache.username, Cache.password);
        authToken = responseJson['token'];
        Cache().authToken = authToken;
      }
      var uri = Uri.parse(url);
      HttpClient client = new HttpClient();

      client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);

      final HttpClientRequest request = await client.postUrl(uri);
      request.headers.contentType = new ContentType("application", "json", charset: "utf-8");
      request.headers.set('Authorization', 'Bearer $authToken');
      //request.contentLength = body.length;
      request.write(body);
      HttpClientResponse result = await request.close();
      client.close();
      trace(" ------------post response code: " + result.statusCode.toString());
      if (result.statusCode == 200) {
        return await result.transform(utf8.decoder).join();
      } else if (result.statusCode == 404) {
        throw NotFoundException(message: "Not Found");
      } else {
        throw Exception("Network Error, please try again later");
      }
    } on Exception catch (e) {
      print("Post Exception: $e");
      throw Exception("Network Error, please try again later");
    }
  }

  Future<String> get(String endPoint, String param) async {
    final url = Cache().getBaseUrl() + endPoint + param;

    trace("-------- API get " + url);
    String authToken = Cache().getAuthToken();
    if (authToken.length == 0) {
      var responseJson = await this.authenticateUser(Cache.username, Cache.password);
      authToken = responseJson['token'];
      Cache().authToken = authToken;
    }
    var uri = Uri.parse(url);

    HttpClient client = new HttpClient();

    client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);

    final HttpClientRequest request = await client.getUrl(uri);
    request.headers.set('Content-Type', 'application/json');
    request.headers.set('Authorization', 'Bearer $authToken');
    HttpClientResponse result = await request.close();
    client.close();
    trace(" ------------get response code: " + result.statusCode.toString());
    if (result.statusCode == 200) {
      return await result.transform(utf8.decoder).join();
    } else if (result.statusCode == 404) {
      throw NotFoundException(message: "Not Found");
    } else {
      throw Exception("Network Error, please try again later");
    }
  }
}