/*
This is api provider
This page is used to get data from API
 */

import 'dart:io';

import 'package:dio/dio.dart';
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';

import 'package:mobdr/config/constant.dart';
import 'package:mobdr/main.dart';
import 'package:mobdr/service/shared_prefs.dart';

class ApiProvider {
  Dio dio = Dio();
  String connErr = 'Please check your internet connection and try again';

  Future<Response> getCrud(url, body) async {
    print('url : ' + url.toString());
    try {
      dio.options.headers['content-Type'] = 'application/json';
      dio.options.headers['Accept'] = 'application/json';
      dio.options.headers['Cookie'] = "pguid=${SharedPrefs().guid}";

      dio.options.connectTimeout = Duration(seconds: 5);
      dio.options.receiveTimeout = Duration(seconds: 4);

      if (body != null) {
        dio.options.queryParameters = body;
      }

      return await dio.get(url);
    } on DioError catch (e) {
      //print(e.toString()+' | '+url.toString());
      if (e.type == DioErrorType.badResponse) {
        int? statusCode = e.response!.statusCode;
        if (statusCode == STATUS_NOT_FOUND) {
          throw "Api not found";
        } else if (statusCode == STATUS_INTERNAL_ERROR) {
          throw "Internal Server Error";
        } else {
          throw e.error.toString();
        }
      } else if (e.type == DioErrorType.connectionTimeout) {
        throw e.message.toString();
      } else if (e.type == DioErrorType.cancel) {
        throw 'cancel';
      }
      throw connErr;
    } finally {
      //dio.close(); // TODO: Attention pas close
    }
  }

  Future<Response> getConnect(url, apiToken) async {
    print('url : ' + url.toString());
    try {
      dio.options.headers['content-Type'] = 'application/x-www-form-urlencoded';
      dio.options.connectTimeout = Duration(seconds: 5);
      dio.options.receiveTimeout = Duration(seconds: 4);

      return await dio.post(url, cancelToken: apiToken);
    } on DioError catch (e) {
      //print(e.toString()+' | '+url.toString());
      if (e.type == DioErrorType.badResponse) {
        int? statusCode = e.response!.statusCode;
        if (statusCode == STATUS_NOT_FOUND) {
          throw "Api not found";
        } else if (statusCode == STATUS_INTERNAL_ERROR) {
          throw "Internal Server Error";
        } else {
          throw e.error.toString();
        }
      } else if (e.type == DioErrorType.connectionTimeout) {
        throw e.message.toString();
      } else if (e.type == DioErrorType.cancel) {
        throw 'cancel';
      }
      throw connErr;
    } finally {
      //dio.close(); // TODO: Attention pas close
    }
  }

  Future<Response> postConnect(url, data, apiToken) async {
    print('url : ' + url.toString());
    print('postData : ' + data.toString());
    try {
      dio.options.headers['content-Type'] = 'application/x-www-form-urlencoded';
      dio.options.connectTimeout = Duration(seconds: 5);
      dio.options.receiveTimeout = Duration(seconds: 4);

      return await dio.post(url, data: data, cancelToken: apiToken);
    } on DioError catch (e) {
      //print(e.toString()+' | '+url.toString());
      if (e.type == DioErrorType.badResponse) {
        int? statusCode = e.response!.statusCode;
        if (statusCode == STATUS_NOT_FOUND) {
          throw "Api not found";
        } else if (statusCode == STATUS_INTERNAL_ERROR) {
          throw "Internal Server Error";
        } else {
          throw e.error.toString();
        }
      } else if (e.type == DioErrorType.connectionTimeout) {
        throw e.message.toString();
      } else if (e.type == DioErrorType.cancel) {
        throw 'cancel';
      }
      throw connErr;
    } finally {
      //dio.close(); // TODO: Attention pas close
    }
  }

  /// login function
  Future<String> login(
      String userName, String pinCode, String securityCode) async {
    var body = {
      "application": "MobDR ", //+ ExeInfo(exeVersion)
      "pin_code": md5.convert(utf8.encode(pinCode)),
      "security_code": securityCode, //sCodeSecurite
      "langage": "fr",
      "screenheight": "0", //SysYRes()
      "screenwidth": "0", //SysXRes()
      "browser_name": "Natif",
      "browser_version": "Application",
      "engine_name": "Android",
      "device_model": "",
      "device_type": "mobile", //SysInfoAppareil(sysModele) WDM 23
      "device_vendor": "", //SysInfoAppareil(sysFabricant) WDM 23
      "ismobile": 1,
      "engine_version": "", // SysVersionAndroid(sysVersionApiLevel)
      "os_name": "", // SysVersionAndroid(sysVersionPlateForme)
      "os_version": "", //SysVersionAndroid(sysVersionNumÈro)
      "fingerprint":
          "aa" // TODO: on peut mettre un fingerprint sur la version ou sur l'heure
    };
    Response response;

    try {
      response = await getCrud(
          ApiConstants.baseUrl +
              ApiConstants.mp4Endpoint +
              ApiConstants.restEndpoint +
              '/login/' +
              userName +
              '/guid',
          body);

      print('res : ' + response.toString());

      switch (response.data['autorisation']) {
        case 1:

          /// save user data
          SharedPrefs().id_utilisateur = response.data['id_utilisateur'];
          SharedPrefs().email = response.data['email'];
          SharedPrefs().expire = response.data['expire'];
          SharedPrefs().guid = response.data['guid'];
          SharedPrefs().langage = response.data['langage'];
          SharedPrefs().last_traduction = response.data['last_traduction'];
          SharedPrefs().login = response.data['login'];
          SharedPrefs().nom = response.data['nom'];
          SharedPrefs().prenom = response.data['prenom'];
          SharedPrefs().version = response.data['version'];
          SharedPrefs().photo = response.data['photo'];

          /// get image from url
          Response ReponseImg =
              await dio.get(ApiConstants.baseUrl + response.data['photo'],
                  options: Options(
                    responseType: ResponseType.bytes,
                    contentType: 'application/octet-stream',
                  ));

          /// convert bytes to base64 string
          String base64Photo = base64.encode(ReponseImg.data);

          /// create box user
          objectbox.userBox.removeAll();
          objectbox.addUSer(
              response.data['id_utilisateur'],
              response.data['login'],
              response.data['nom'],
              response.data['prenom'],
              base64Photo);

          return 'OK';
        case -1:
          return 'Compte désactivé ...';
        case -2:
          return 'SECURITY_CODE';
        case -3:
          return 'Téléphone bloqué !';
        case -4:
          return 'Code PIN incorrect !';
        default:
          return "Une erreur inconnue s''est produite";
      }
    } catch (ex) {
      return ex.toString(); // return ex.response!.data;
    }
  }

  /// Synchronize all informations about store, competitor, calendar
  Future<String> SyncCalendar() async {
    try {
      final responseFutures = Future.wait([
        getCrud(
          ApiConstants.baseUrl +
              ApiConstants.externalEndpoint +
              ApiConstants.restEndpoint +
              '/mobDR/etablissement',
          null,
        ),
        getCrud(
          ApiConstants.baseUrl +
              ApiConstants.externalEndpoint +
              ApiConstants.restEndpoint +
              '/mobDR/etablissement/concurrent',
          null,
        ),
        getCrud(
          ApiConstants.baseUrl +
              ApiConstants.externalEndpoint +
              ApiConstants.restEndpoint +
              '/mobDR/visite/calendrier',
          {"id_utilisateur": 6, "start": 20230101, "end": 20230531},
        ),
        getCrud(
          ApiConstants.baseUrl +
              ApiConstants.externalEndpoint +
              ApiConstants.restEndpoint +
              '/mobDR/visite/tag',
          null,
        ),
        getCrud(
            ApiConstants.baseUrl +
                ApiConstants.externalEndpoint +
                ApiConstants.restEndpoint +
                '/mobDR/visite/typologie',
            null),
      ]);

      final List<Response<dynamic>> responses =
          await responseFutures.timeout(Duration(seconds: 30));

      final etabResponse = responses[0];
      final competitorResponse = responses[1];
      final visitResponse = responses[2];
      final visitTagResponse = responses[3];
      final PhotoTypologyResponse = responses[4];

      if (etabResponse.statusCode == STATUS_OK) {
        // remove all objects
        objectbox.etabBox.removeAll();

        // fill box "Etabs"
        objectbox.addEtabs(etabResponse.data['boutiques']);
      }

      if (competitorResponse.statusCode == STATUS_OK) {
        // remove all objects
        objectbox.etabCompetitorBox.removeAll();

        // fill box "Etabs"
        objectbox.addEtabCompetitors(competitorResponse.data['concurrents']);
      }

      if (visitResponse.statusCode == STATUS_OK) {
        /// fill box "visite"
        objectbox.syncVisits(visitResponse.data['events']);
      }

      if (visitTagResponse.statusCode == STATUS_OK) {
        // remove all objects
        objectbox.visitTagBox.removeAll();

        /// fill box "visiteTag"
        objectbox.addVisitTags(visitTagResponse.data['tags']);
      }

      if (PhotoTypologyResponse.statusCode == STATUS_OK) {
        // remove all objects
        objectbox.PhotoTypologyBox.removeAll();

        /// fill box "PhotoTypology"
        objectbox.addPhotoTypologies(PhotoTypologyResponse.data['typologies']);
      }

      if (etabResponse.statusCode == STATUS_OK &&
          competitorResponse.statusCode == STATUS_OK &&
          visitResponse.statusCode == STATUS_OK &&
          visitTagResponse.statusCode == STATUS_OK &&
          PhotoTypologyResponse.statusCode == STATUS_OK) {
        return 'OK';
      } else {
        return etabResponse.statusMessage ??
            competitorResponse.statusMessage ??
            visitResponse.statusMessage ??
            visitTagResponse.statusMessage ??
            PhotoTypologyResponse.statusMessage ??
            'Unknown error';
      }
    } catch (ex) {
      return ex.toString();
    }
  }

  /// Synchronize "Photos"
  Future<String> SyncPhotos() async {
    Response response;

    try {
      /// get "Photo typologies"
      response = await getCrud(
          ApiConstants.baseUrl +
              ApiConstants.externalEndpoint +
              ApiConstants.restEndpoint +
              '/mobDR/visite/typologie',
          null);

      if (response.statusCode == STATUS_OK) {
        // remove all objects
        objectbox.PhotoTypologyBox.removeAll();

        /// fill box "Photo typologies"
        objectbox.addPhotoTypologies(response.data['typologies']);
      }

      /// all ok
      if (response.statusCode == STATUS_OK) {
        return 'OK';
      } else {
        return response.statusMessage ?? 'Unknow error ...';
      }
    } catch (ex) {
      return ex.toString(); // return ex.response!.data;
    }
  }

  Future<int> uploadPhotoServlet(int id_visite, String photoPath) async {
    try {
      final url = Uri.parse(SERVLET_API);
      final file = File(photoPath);
      final bytes = await file.readAsBytes();

      final multipartRequest = http.MultipartRequest('POST', url)
        ..fields['id_visite'] = id_visite.toString()
        ..files.add(http.MultipartFile.fromBytes(
          'photo',
          bytes,
          filename: path.basename(photoPath),
          contentType: MediaType('image', 'jpeg'),
        ));

      final headers = {'Cookie': "pguid=${SharedPrefs().guid};"};
      multipartRequest.headers.addAll(headers);

      final response = await multipartRequest.send();
      final responseString = await response.stream.bytesToString();

      final jsonResponse = jsonDecode(responseString);
      final idPhotoString = jsonResponse[0]['id_photo'];
      final idPhoto = int.parse(idPhotoString);

      return idPhoto;
    } catch (e) {
      print('Error uploading photo: $e');
      return -1;
    }
  }

  Future<bool> updatePhotoTypology(
      int id_visite, int id_photo_mp4, int id_photo_typologie) async {
    final url =
        '${ApiConstants.baseUrl}${ApiConstants.mp4Endpoint}${ApiConstants.restEndpoint}/visite/$id_visite/photo/$id_photo_mp4/tri/typo';

    final headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/json',
      'Cookie': 'pguid=${SharedPrefs().guid};',
    };

    final params = {
      'id_typologie': id_photo_typologie.toString(),
    };

    final response =
        await http.put(Uri.parse(url), headers: headers, body: params);

    if (response.statusCode == 200) {
      return true;
    } else {
      return false;
    }
  }

  Future<bool> updatePhotoVisibility(int id_visite, int id_photo_mp4,
      int photo_principale, int photo_privee) async {
    final url =
        '${ApiConstants.baseUrl}${ApiConstants.mp4Endpoint}${ApiConstants.restEndpoint}/visite/$id_visite/photo/$id_photo_mp4/tri/visibilite';

    final headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/json',
      'Cookie': 'pguid=${SharedPrefs().guid};',
    };

    final params = {
      'principale': photo_principale.toString(),
      'privee': photo_privee.toString(),
    };

    final response =
        await http.put(Uri.parse(url), headers: headers, body: params);

    if (response.statusCode == 200) {
      return true;
    } else {
      return false;
    }
  }

  Future<bool> updatePhotoTags(
      int id_visite, int id_photo_mp4, String tags) async {
    final url =
        '${ApiConstants.baseUrl}${ApiConstants.mp4Endpoint}${ApiConstants.restEndpoint}/visite/$id_visite/photo/$id_photo_mp4/tri/tags';

    final headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/json',
      'Cookie': 'pguid=${SharedPrefs().guid};',
    };

    final params = {'tags': tags};

    final response =
        await http.put(Uri.parse(url), headers: headers, body: params);

    if (response.statusCode == 200) {
      return true;
    } else {
      return false;
    }
  }

  Future<bool> updatePhotoConpetitor(
      int id_visite, int id_photo_mp4, int id_concurrence_lien) async {
    final url =
        '${ApiConstants.baseUrl}${ApiConstants.mp4Endpoint}${ApiConstants.restEndpoint}/visite/$id_visite/photo/$id_photo_mp4/tri/concurrence';

    final headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/json',
      'Cookie': 'pguid=${SharedPrefs().guid};',
    };

    final params = {
      'id_concurrence_lien': id_concurrence_lien.toString(),
    };

    final response =
        await http.put(Uri.parse(url), headers: headers, body: params);

    if (response.statusCode == 200) {
      return true;
    } else {
      return false;
    }
  }

  Future<String> getExample(apiToken) async {
    Response response;

    response =
        await getConnect(ApiConstants.baseUrl + '/example/getData', apiToken);
    print('res : ' + response.toString());
    return response.data.toString();
  }

  Future<String> postExample(String id, apiToken) async {
    Response response;

    var postData = {'id': id};
    response = await postConnect(
        ApiConstants.baseUrl + '/example/postData', postData, apiToken);
    print('res : ' + response.toString());
    return response.data.toString();
  }
  /*
  Future<List<StudentModel>> getStudent(String sessionId, apiToken) async {
    var postData = {'session_id': sessionId};
    response = await postConnect(
        ApiConstants.baseUrl + '/student/getStudent', postData, apiToken);
    if (response.data['status'] == STATUS_OK) {
      List responseList = response.data['data'];
      List<StudentModel> listData =
          responseList.map((f) => StudentModel.fromJson(f)).toList();
      return listData;
    } else {
      throw response.data['msg'];
    }
  }
  */

  Future<List<dynamic>> addStudent(
      String sessionId,
      String studentName,
      String studentPhoneNumber,
      String studentGender,
      String studentAddress,
      apiToken) async {
    var postData = {
      'session_id': sessionId,
      'student_name': studentName,
      'student_phone_number': studentPhoneNumber,
      'student_gender': studentGender,
      'student_address': studentAddress,
    };
    Response response;

    response = await postConnect(
        ApiConstants.baseUrl + '/student/addStudent', postData, apiToken);
    if (response.data['status'] == STATUS_OK) {
      List<dynamic> respList = [];
      respList.add(response.data['msg']);
      respList.add(response.data['data']['id']);
      return respList;
    } else {
      throw response.data['msg'];
    }
  }

  Future<String> editStudent(
      String sessionId,
      int studentId,
      String studentName,
      String studentPhoneNumber,
      String studentGender,
      String studentAddress,
      apiToken) async {
    var postData = {
      'session_id': sessionId,
      'student_id': studentId,
      'student_name': studentName,
      'student_phone_number': studentPhoneNumber,
      'student_gender': studentGender,
      'student_address': studentAddress,
    };
    Response response;

    response = await postConnect(
        ApiConstants.baseUrl + '/student/editStudent', postData, apiToken);
    if (response.data['status'] == STATUS_OK) {
      return response.data['msg'];
    } else {
      throw response.data['msg'];
    }
  }

  Future<String> deleteStudent(
      String sessionId, int studentId, apiToken) async {
    var postData = {
      'session_id': sessionId,
      'student_id': studentId,
    };
    Response response;
    response = await postConnect(
        ApiConstants.baseUrl + '/student/deleteStudent', postData, apiToken);
    if (response.data['status'] == STATUS_OK) {
      return response.data['msg'];
    } else {
      throw response.data['msg'];
    }
  }

  /*
  Future<List<LoginModel>> login2(
      String email, String password, apiToken) async {
    var postData = {
      'email': email,
      'password': password,
    };
    response = await postConnect(LOGIN_API, postData, apiToken);
    if (response.data['status'] == STATUS_OK) {
      List responseList = response.data['data'];
      List<LoginModel> listData =
          responseList.map((f) => LoginModel.fromJson(f)).toList();
      return listData;
    } else {
      throw response.data['msg'];
    }
  }
  */

  /*
  Future<List<ProductGridModel>> getProductGrid(
      String sessionId, String skip, String limit, apiToken) async {
    var postData = {'session_id': sessionId, 'skip': skip, 'limit': limit};
    response = await postConnect(PRODUCT_API, postData, apiToken);
    if (response.data['status'] == STATUS_OK) {
      List responseList = response.data['data'];
      //print('data : '+responseList.toString());
      List<ProductGridModel> listData =
          responseList.map((f) => ProductGridModel.fromJson(f)).toList();
      return listData;
    } else {
      throw response.data['msg'];
    }
  }
  */

  /*
  Future<List<ProductListviewModel>> getProductListview(
      String sessionId, String skip, String limit, apiToken) async {
    var postData = {'session_id': sessionId, 'skip': skip, 'limit': limit};
    response = await postConnect(PRODUCT_API, postData, apiToken);
    if (response.data['status'] == STATUS_OK) {
      List responseList = response.data['data'];
      //print('data : '+responseList.toString());
      List<ProductListviewModel> listData =
          responseList.map((f) => ProductListviewModel.fromJson(f)).toList();
      return listData;
    } else {
      throw response.data['msg'];
    }
  }
  */
}
