import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';

import 'package:fis_common/logger/logger.dart';
import 'package:fis_i18n/i18n.dart';
import 'package:fis_jsonrpc/rpc.dart';
import 'package:flyinsono/architecture/states/states.dart';
import 'package:flyinsono/architecture/utils/prompt_box.dart';
import 'package:flyinsono/rpc.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:xml/xml.dart';

import 'large_file_uploader.dart';

/// 部分分段返回的结果
class PartFileResultInfo {
  /// 分片上传的文件地址
  String fileUrl;

  /// 分片上传的eTag
  String eTag;

  /// 分块的uploadId
  String uploadId;

  /// 分块的下标
  int partNumber;

  PartFileResultInfo({
    required this.fileUrl,
    required this.eTag,
    required this.uploadId,
    required this.partNumber,
  });
}

/// 分段上传文件
class PartUploadFileInfo {
  /// 分片的下标
  int partNumber;

  /// 初始化生成的uploadId
  String uploadId;

  /// 分片文件的二进制数据
  Uint8List fileBytes;

  PartUploadFileInfo({
    required this.partNumber,
    required this.uploadId,
    required this.fileBytes,
  });
}

///存储上传文件基本信息
class StorageUploadFileInfo {
  ///文件token
  String fileToken;

  ///文件的二进制数据
  Uint8List fileBytes;

  ///文件名
  String fileName;

  ///文件模拟类型
  String? mimeType;

  /// 文件大小
  int? fileSize;

  StorageUploadFileInfo({
    required this.fileToken,
    required this.fileBytes,
    required this.fileName,
    this.mimeType,
    this.fileSize,
  });
}

///存储上传请求
class StorageUploadRequest {
  StorageUploadFileInfo fileInfo;

  StorageUploadRequest({
    required this.fileInfo,
  });
}

///web的存储API
class StorageWebApi {
  Future<bool> testApi() async => false;
}

///存储服务扩展类
extension StorageServiceExt on StorageService {
  /// TODO BAKA
  /// 1、这边暂时等分片上传的方案，每次调用一次鉴权不合理
  /// 2、需要封装下
  /// 结束分片上传
  Future<String> completeMulitPartUploadFileAsync(
    String name,
    String uploadId,
    List<PartFileResultInfo> partFileResultInfoList, [
    bool? isRechristen,
  ]) async {
    try {
      final auth = await getAuth(
        fileName: name,
        isRechristen: false,
        headerParams: [
          DataItemDTO(key: "Content-Type", value: "application/xml")
        ],
        urlParams: [
          DataItemDTO(
            key: 'uploadId',
            value: uploadId,
          ),
        ],
        requestMethod: 'post',
        // 这边授权不同
      );
      String url = auth.storageUrl! + '?uploadId=$uploadId';
      String xmlBody = '<CompleteMultipartUpload>';
      for (int i = 1; i <= partFileResultInfoList.length; i++) {
        xmlBody += '<Part>';
        xmlBody += '<PartNumber>$i</PartNumber>';
        xmlBody += '<ETag>${partFileResultInfoList[i - 1].eTag}</ETag>';
        xmlBody += '</Part>';
      }
      xmlBody += '</CompleteMultipartUpload>';

      final client = http.Client();
      final request = http.Request('POST', Uri.parse(url));
      request.headers['Authorization'] = auth.authorization!;
      request.headers['Content-Type'] = 'application/xml';
      request.bodyBytes = utf8.encode(xmlBody); // 手动将 XML 字符串转换为字节数组

      final response = await client.send(request);
      if (response.statusCode == 200) {
        String location = '';
        final xmlDoc = XmlDocument.parse(
            await response.stream.bytesToString()); // 使用 XML 解析器解析响应体
        final root = xmlDoc.rootElement;
        final nodes = root.findElements('Location');
        if (nodes.isNotEmpty) {
          location = nodes.first.text.replaceAll('"', '');
        }
        return location;
      }
    } catch (e) {
      logger.e('StorageServiceExt multiPartUploadAsync ex:$e');
    }
    return '';
  }

  ///鉴权 TODO: fileName 为空则接口报错，所以此处设置一个默认值
  Future<StorageServiceSettingDTO> getAuth({
    String? fileName,
    bool? isRechristen,
    List<DataItemDTO>? urlParams,
    List<DataItemDTO>? headerParams,
    String? requestMethod,
  }) async {
    try {
      final result = await rpc.storage.getAuthorizationAsync(FileServiceRequest(
        token: Store.user.token,
        fileName: fileName ?? "dat",
        isRechristen: isRechristen ?? true,
        urlParams: urlParams,
        headerParams: headerParams,
        requestMethod: requestMethod,
      ));
      return result;
    } catch (e) {
      return StorageServiceSettingDTO();
    }
  }

  /// TODO BAKA
  /// 1、这边暂时等分片上传的方案，每次调用一次鉴权不合理
  /// 2、需要封装下
  /// 初始化分片
  /// [name] 文件名 名称
  Future<String?> initiateMultipartUploadResult(
    String name,
  ) async {
    try {
      final auth = await getAuth(
        fileName: name,
        requestMethod: 'post',
        isRechristen: false,
        // 这边授权不同
      );
      String url = auth.storageUrl! + '?uploads';
      Map<String, String> params = {
        'Authorization': auth.authorization!,
        'ContentType': 'application/x-zip-compressed',
        'ContentLength': '0',
      };
      final response = await http
          .post(
            Uri.parse(url),
            headers: params,
          )
          .timeout(
            const Duration(seconds: 30),
          );
      if (response.statusCode == 200) {
        String uploadId = '';
        final xmlDoc = XmlDocument.parse(response.body);
        final root = xmlDoc.rootElement;
        final nodes = root.findElements('UploadId');
        if (nodes.isNotEmpty) {
          uploadId = nodes.first.text.replaceAll('"', '');
        }

        return uploadId;
      }
    } catch (e) {
      logger.e('StorageServiceExt initiateMultipartUploadResult ex:$e');
    }
    return null;
  }

  /// TODO BAKA
  /// 1、这边暂时等分片上传的方案，每次调用一次鉴权不合理
  /// 2、需要封装下
  /// 分片上传
  Future<PartFileResultInfo> multiPartUploadAsync(
    String name,
    PartUploadFileInfo partUploadFileInfo,
    StorageServiceSettingDTO storageServiceSetting, [
    bool? isRechristen,
  ]) async {
    try {
      String url = storageServiceSetting.storageUrl! +
          '?partNumber=${partUploadFileInfo.partNumber}&uploadId=${partUploadFileInfo.uploadId}';
      Map<String, String> params = {
        'Authorization': storageServiceSetting.authorization!,
        'ContentType': 'application/octet-stream',
      };
      final response = await http
          .put(
            Uri.parse(url),
            body: partUploadFileInfo.fileBytes,
            headers: params,
          )
          .timeout(
            const Duration(seconds: 30),
          );
      if (response.statusCode == 200) {
        String etag = response.headers['etag'] ?? '';

        return PartFileResultInfo(
          eTag: etag,
          fileUrl: storageServiceSetting.storageUrl ?? '',
          uploadId: partUploadFileInfo.uploadId,
          partNumber: partUploadFileInfo.partNumber,
        );
      }
    } catch (e) {
      logger.e('StorageServiceExt multiPartUploadAsync ex:$e');
    }
    return PartFileResultInfo(
      eTag: '',
      fileUrl: storageServiceSetting.storageUrl ?? '',
      uploadId: partUploadFileInfo.uploadId,
      partNumber: -1,
    );
  }

  /// 分片上传获取权限
  Future<StorageServiceSettingDTO> multiPartUploadGetAuth(
    String name,
    PartUploadFileInfo partUploadFileInfo,
  ) async {
    final StorageServiceSettingDTO? auth = await getAuth(
      fileName: name,
      isRechristen: false,
      urlParams: [
        DataItemDTO(
          key: 'uploadId',
          value: partUploadFileInfo.uploadId,
        ),
      ],
    );
    return auth ?? StorageServiceSettingDTO();
  }

  ///文件上传
  Future<String?> upload(XFile xfile) async {
    try {
      String? name;
      if (contentTypes.values.contains(xfile.mimeType)) {
        for (var key in contentTypes.keys) {
          if (contentTypes[key] == xfile.mimeType) {
            name = key;
            break;
          }
        }
      }
      final auth = await getAuth(fileName: name);
      final bytes = await xfile.readAsBytes();
      Map<String, String> params = {};
      params['Authorization'] = auth.authorization!;
      params['ContentType'] = auth.contentType!;

      final response = await http
          .put(
            Uri.parse(auth.storageUrl!),
            body: bytes,
            headers: params,
          )
          .timeout(
            const Duration(seconds: 30),
          );
      if (response.statusCode == 200) {
        return auth.storageUrl;
      }
    } catch (e) {
      PromptBox.toast(i18nBook.common.uploadFail.t);
      logger.e("file upload error", e);
      return null;
    }
    return null;
  }

  ///文件上传(base64)
  Future<String?> uploadBase64File(String base64Str, String name) async {
    try {
      var buffer = base64.decode(base64Str);
      return uploadUint8List(buffer, name);
    } catch (e) {
      logger.e("file upload error", e);
    }
    return null;
  }

  ///文件上传（UInt8List）
  Future<String?> uploadUint8List(Uint8List buffer, String name,
      [bool? isRechristen]) async {
    try {
      var nameInfos = name.split('.');
      final auth = await getAuth(
        fileName: nameInfos.last,
        isRechristen: isRechristen,
      );
      Map<String, String> params = {};
      params['Authorization'] = auth.authorization!;
      params['ContentType'] = auth.contentType!;
      final Duration timeout = countTimeoutByFileSize(buffer.lengthInBytes);
      final response = await http
          .put(
            Uri.parse(auth.storageUrl!),
            body: buffer,
            headers: params,
          )
          .timeout(timeout);
      if (response.statusCode == 200) {
        return auth.storageUrl;
      }
    } catch (e) {
      logger.e('StorageServiceExt uploadUint8List ex:$e');
    }
    return null;
  }

  /// 自动设置上传超时
  /// 0-3M 30s
  /// 3-10M 90s
  /// 10-20M 180s
  /// 20M+ 300s
  Duration countTimeoutByFileSize(int lengthInBytes) {
    print("Upload file size：${lengthInBytes / 1024 / 1024} MB");
    if (lengthInBytes < 1024 * 1024 * 3) {
      print("SetTimeout：30s");
      return const Duration(seconds: 30);
    } else if (lengthInBytes < 1024 * 1024 * 10) {
      print("SetTimeout：60s");
      return const Duration(seconds: 90);
    } else if (lengthInBytes < 1024 * 1024 * 20) {
      print("SetTimeout：120s");
      return const Duration(seconds: 180);
    } else {
      print("SetTimeout：300s");
      return const Duration(seconds: 300);
    }
  }
}
