import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:device_info/device_info.dart';
import 'package:dio/dio.dart';
import 'package:fcs/domain/vo/status.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';

import '../config.dart';
import 'dev_info.dart';
import 'firebase_helper.dart';

final log = Logger('requestAPI');

// request makes http request
// if token is null
Future<dynamic> requestAPI(
  String path,
  method, {
  dynamic payload,
  String token,
  String url,
}) async {
  DevInfo devInfo = await DevInfo.getDevInfo();

  String deviceName = "${devInfo.model}(${devInfo.id})";
  log.info("device:${devInfo.deviceID},deviceName:$deviceName");

  Map<String, dynamic> headers = {};
  if (token != null) {
    headers["Token"] = token;
  }
  if (devInfo != null && devInfo.deviceID != null && deviceName != null) {
    headers["Device"] = devInfo.deviceID + ":" + deviceName;
  }
  headers["Project-ID"] = Config.instance.reportProjectID;

  BaseOptions options = new BaseOptions(
    method: method,
    baseUrl: url == null ? Config.instance.apiURL : url,
    connectTimeout: 10000,
    receiveTimeout: 60000,
    headers: headers,
  );
  log.info("baseUrl:${options.baseUrl}, path:$path");
  try {
    Dio dio = new Dio(options);
    Response response = await dio.request(
      path,
      data: payload,
    );
    var data = Status.fromJson(response.data);
    if (data.status == 'Ok') {
      return response.data["data"];
    } else {
      throw Exception(data.message);
    }
  } catch (e) {
    log.warning("path:$path, api:$e");
    throw e;
  }
}

// request makes http request
// if token is null
Future<dynamic> requestDownloadAPI(String path, method,
    {dynamic payload, String token, String url, String filePath}) async {
  DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
  AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
  String deviceName = "${androidInfo.model}(${androidInfo.id})";
  log.info("device:${androidInfo.androidId},deviceName:$deviceName");

  var bytes = utf8.encode(payload);
  var base64Str = base64.encode(bytes);
  String escapePayload = HtmlEscape().convert(base64Str);

  try {
    String baseUrl = url == null ? Config.instance.apiURL : url;
    log.info("Path:$baseUrl$path");
    HttpClient client = new HttpClient();
    var _downloadData = StringBuffer();
    var fileSave = new File(filePath);
    var request = await client.getUrl(Uri.parse("$baseUrl$path"));
    request.headers.set("Project-ID", Config.instance.reportProjectID);
    request.headers
        .set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8");
    if (token != null) {
      request.headers.set("Token", token);
    }
    if (androidInfo.androidId != null) {
      request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
    }
    request.headers.set("payload", escapePayload);
    var response = await request.close();
    print("headers:${response.headers}");
    response.transform(utf8.decoder).listen((d) => _downloadData.write(d),
        onDone: () {
      fileSave.writeAsString(_downloadData.toString());
    });
  } catch (e) {
    log.warning("path:$path, api:$e");
    throw e;
  }
}

// request makes http request
// if token is null
Future<dynamic> requestDownloadPDFAPI(String path, method,
    {dynamic payload, String token, String url, String filePath}) async {
  DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
  AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
  String deviceName = "${androidInfo.model}(${androidInfo.id})";
  log.info("device:${androidInfo.androidId},deviceName:$deviceName");

  var bytes = utf8.encode(payload);
  var base64Str = base64.encode(bytes);
  String escapePayload = HtmlEscape().convert(base64Str);

  try {
    String baseUrl = url == null ? Config.instance.apiURL : url;
    log.info("Path:$baseUrl$path");
    HttpClient client = new HttpClient();
    // var _downloadData = StringBuffer();
    var fileSave = new File(filePath);
    var request = await client.getUrl(Uri.parse("$baseUrl$path"));
    request.headers.set("Project-ID", Config.instance.reportProjectID);
    if (token != null) {
      request.headers.set("Token", token);
    }
    if (androidInfo.androidId != null) {
      request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
    }
    request.headers.set("payload", escapePayload);
    var response = await request.close();
    print("headers:${response.headers}");
    var _downloadData = List<int>();

    response.listen((d) => _downloadData.addAll(d), onDone: () {
      fileSave.writeAsBytes(_downloadData);
    });
    // response.transform(utf8.decoder).listen((d) => _downloadData.write(d),
    //     onDone: () {
    //   fileSave.writeAsString(_downloadData.toString());
    // });
  } catch (e) {
    log.warning("path:$path, api:$e");
    throw e;
  }
}

typedef OnDownloadDone(File file);
// request makes http request
// if token is null
Future<dynamic> requestDownload(String path, method,
    {dynamic payload,
    String token,
    String url,
    String filePath,
    OnDownloadDone onDownloadDone}) async {
  DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
  AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
  String deviceName = "${androidInfo.model}(${androidInfo.id})";
  log.info("device:${androidInfo.androidId},deviceName:$deviceName");

  var bytes = utf8.encode(payload);
  var base64Str = base64.encode(bytes);
  String escapePayload = HtmlEscape().convert(base64Str);

  try {
    String baseUrl = url == null ? Config.instance.apiURL : url;
    log.info("Path:$baseUrl$path");
    HttpClient client = new HttpClient();
    // var _downloadData = StringBuffer();
    var request = await client.postUrl(Uri.parse("$baseUrl$path"));
    request.headers.set("Project-ID", Config.instance.reportProjectID);
    if (token != null) {
      request.headers.set("Token", token);
    }
    if (androidInfo.androidId != null) {
      request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
    }
    request.headers
        .set(HttpHeaders.contentTypeHeader, 'application/json; charset=utf-8');
    request.headers.set("payload", escapePayload);
    request.write(payload);
    // request.write(escapePayload);
    var response = await request.close();
    print("headers:${response.headers}");
    var _downloadData = List<int>();
    var cd = response.headers.value("content-disposition");
    String fileName = "download.csv";
    if (cd != null && cd.contains("filename=")) {
      fileName = cd.substring(cd.indexOf("=") + 1);
    }

    var file = filePath + "/" + fileName;
    var fileSave = new File(filePath + "/" + fileName);
    response.listen((d) => _downloadData.addAll(d), onDone: () async {
      await fileSave.writeAsBytes(_downloadData);
      if (onDownloadDone != null) {
        onDownloadDone(fileSave);
      }

      // final message = await OpenFile.open(file);
      // log.info("Open file result:${message.message}");
      // if (message.message != "done") {
      //   throw Exception(message.message);
      // }
      // await Share.file(fileName, fileName, _downloadData,
      //     downloadType == DownloadType.CSV ? "text/csv" : "application/pdf");
    });
  } catch (e) {
    e.toString();
    log.warning("path:$path, api:$e");
    throw e;
  }
}

Future<File> downloadPDF(
    String templateName, Map<String, dynamic> values) async {
  final completer = Completer<File>();

  final directory = await getApplicationSupportDirectory();
  String path = ('${directory.path}');
  log.info("download file path:$path");

  var data = {"template_name": templateName, "data": values};
  print("payload:$data");

  await requestDownload("/api/pdf", "POST",
      filePath: path,
      url: Config.instance.reportURL,
      token: await getToken(),
      payload: jsonEncode(data), onDownloadDone: (f) async {
    completer.complete(f);
  });
  return completer.future;
}
