import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:workouttest_util/service/webapi.dart';
import 'package:workouttest_util/util/logging.dart';
import 'package:workouttest_util/util/common.dart';
import 'package:workouttest_util/util/not_found_exception.dart';
import 'package:workouttest_util/model/cache.dart';
import 'package:workouttest_util/util/track.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 {
    if ( kIsWeb ) {
      APIWebClient webClient = APIWebClient();
      return webClient.authenticateUser(email, password);
    }
    var baseUrl = Cache().getBaseUrl();
    log("Base URL: $baseUrl");
    var url = "${baseUrl}authenticate";

    try {
      var uri = Uri.parse(url);
      HttpClient client = HttpClient(); 
      final HttpClientRequest request = await client.postUrl(uri);
      request.headers.contentType = ContentType('application', 'json', charset: 'utf-8');
      request.headers.set('Authorization', '1');

      final body = jsonEncode(<String, String>{
        'username': email,
        'password': password
      });    

      request.write(body);
      HttpClientResponse result = await request.close();
      client.close();
      if (result.statusCode != 200) {
        log("authentication response: ${result.statusCode} with URL: $url");
        throw Exception("Authentication error: ${result.statusCode}");
      }
      
      String response = await result.transform(utf8.decoder).join();
      log("Authentication status: ${result.statusCode}, response: $response");
      final data = jsonDecode(response);
      return data;
    } catch (exception) {
      log(exception.toString());
      try {
        Track().trackError(exception);
      } on Exception catch (e) {
        log(e.toString());
      }

      throw Exception("Network error, try again later!");
    }
  }

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

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

      final HttpClientRequest request = await client.postUrl(uri);
      request.headers.contentType = 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();
      log(" ------------post response code: ${result.statusCode}");
      if (result.statusCode == 200) {
        return await result.transform(utf8.decoder).join();
      } else if (result.statusCode == 404) {
        throw const NotFoundException(message: "Not Found");
      } else {
        throw Exception("Network Error, please try again later");
      }
    } on NotFoundException catch(e) {
      throw NotFoundException(message: "Not Found ${e.message}");
    } on Exception catch (e) {
      log("Post Exception: $e");
      Track().trackError(e);
      throw Exception("Network Error, please try again later");
    }
  }

  Future<String> get(String endPoint, String param) async {
    if ( kIsWeb ) {
      APIWebClient webClient = APIWebClient();
      return webClient.get(endPoint, param);
    }
    final url = Cache().getBaseUrl() + endPoint + param;
    try {
      log("-------- API get $url");
      String authToken = Cache().getAuthToken();
      if (authToken.isEmpty) {
        var responseJson = await authenticateUser(Cache.username, Cache.password);
        authToken = responseJson['token'];
        Cache().authToken = authToken;
      }
      var uri = Uri.parse(url);

      HttpClient client = HttpClient();

      final HttpClientRequest request = await client.getUrl(uri);
      request.headers.contentType = ContentType("application", "json", charset: "utf-8");
      request.headers.set('Authorization', 'Bearer $authToken');
      request.write("");
      HttpClientResponse result = await request.close();
      client.close();
      log(" ------------get response code: ${result.statusCode}");
      if (result.statusCode == 200) {
        return await result.transform(utf8.decoder).join();
      } else if (result.statusCode == 404) {
        throw const NotFoundException(message: "Not Found");
      } else {
        throw Exception("Network Error, please try again later");
      }
    } on NotFoundException catch(e) {
      throw NotFoundException(message: "Not Found ${e.message}");
    } on Exception catch (e) {
      log("Post Exception: $e");
      Track().trackError(e);
      throw Exception("Network Error, please try again later");
    }
  }
}