import 'dart:async';
import 'dart:io';
import 'package:archive/archive.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;

import 'log/logger.dart';

/// GZIP文件管理器
/// 用于创建和分享GZIP压缩文件，不需要密码保护
class GzipManager {
  static const String TAG = 'GzipManager';
  static final GzipManager _instance = GzipManager._internal();
  factory GzipManager() => _instance;
  GzipManager._internal();

  // 防重复点击机制
  DateTime? _lastGenerateTime;
  String? _lastGeneratedFilePath;
  static const Duration _throttleDuration = Duration(minutes: 1);

  /// 生成GZIP压缩文件并分享
  ///
  /// [filePaths] 要压缩的文件路径列表
  /// [gzipFileName] gzip文件名，默认为 "flashlink_logs_时间戳.tar.gz"
  /// [deleteExisting] 是否删除已存在的同名文件，默认为true
  ///
  /// 返回生成的gzip文件路径，如果失败返回null
  Future<String?> generateAndShareGzip({
    required List<String> filePaths,
    String? gzipFileName,
    bool deleteExisting = true,
  }) async {
    try {
      // 检查防重复点击
      if (_shouldUseLastGeneratedFile()) {
        if (_lastGeneratedFilePath != null &&
            await File(_lastGeneratedFilePath!).exists()) {
          // await _shareFile(_lastGeneratedFilePath!);
          return _lastGeneratedFilePath;
        }
      }

      // 验证文件路径
      final validFilePaths = await _validateFilePaths(filePaths);
      if (validFilePaths.isEmpty) {
        throw Exception('no valid file paths');
      }

      // 生成gzip文件
      final gzipFilePath = await _generateGzip(
        filePaths: validFilePaths,
        gzipFileName: gzipFileName,
        deleteExisting: deleteExisting,
      );

      if (gzipFilePath != null) {
        // 更新防重复点击状态
        _lastGenerateTime = DateTime.now();
        _lastGeneratedFilePath = gzipFilePath;

        // 分享文件
        // await _shareFile(gzipFilePath);
        return gzipFilePath;
      }

      return null;
    } catch (e) {
      log(TAG, 'generate gzip file failed: $e');
      return null;
    }
  }

  /// 检查是否应该使用上次生成的文件
  bool _shouldUseLastGeneratedFile() {
    if (_lastGenerateTime == null) return false;

    final timeDiff = DateTime.now().difference(_lastGenerateTime!);
    return timeDiff < _throttleDuration;
  }

  /// 验证文件路径
  Future<List<String>> _validateFilePaths(List<String> filePaths) async {
    final validPaths = <String>[];

    for (final filePath in filePaths) {
      final file = File(filePath);
      if (await file.exists()) {
        validPaths.add(filePath);
      } else {
        log(TAG, 'file not exists: $filePath');
      }
    }

    return validPaths;
  }

  /// 生成GZIP压缩文件
  Future<String?> _generateGzip({
    required List<String> filePaths,
    String? gzipFileName,
    bool deleteExisting = true,
  }) async {
    try {
      // 获取临时目录
      final tempDir = await getTemporaryDirectory();

      // 生成gzip文件名
      final timestamp = DateTime.now().millisecondsSinceEpoch;
      final fileName = gzipFileName ?? 'flashlink_logs_$timestamp.tar.gz';
      final gzipFilePath = path.join(tempDir.path, fileName);

      // 删除已存在的文件
      if (deleteExisting) {
        final existingFile = File(gzipFilePath);
        if (await existingFile.exists()) {
          await existingFile.delete();
        }
      }

      // 创建GZIP压缩文件
      return await _createGzipFile(
        filePaths: filePaths,
        gzipFilePath: gzipFilePath,
      );
    } catch (e) {
      log(TAG, 'generate gzip file failed: $e');
      return null;
    }
  }

  /// 创建GZIP压缩文件
  Future<String?> _createGzipFile({
    required List<String> filePaths,
    required String gzipFilePath,
  }) async {
    try {
      // 创建TAR归档
      final tarArchive = Archive();

      // 添加文件到TAR归档
      for (final filePath in filePaths) {
        final file = File(filePath);
        final fileName = path.basename(filePath);
        final fileBytes = await file.readAsBytes();

        // 创建TAR文件条目
        final tarFile = ArchiveFile(fileName, fileBytes.length, fileBytes);

        tarArchive.addFile(tarFile);
      }

      // 将TAR归档转换为字节
      final tarData = TarEncoder().encode(tarArchive);

      // 使用GZIP压缩TAR数据
      final gzipData = GZipEncoder().encode(tarData);
      final gzipFile = File(gzipFilePath);
      await gzipFile.writeAsBytes(gzipData);

      log(TAG, 'gzip file generated successfully: $gzipFilePath');
      return gzipFilePath;
    } catch (e) {
      log(TAG, 'create gzip file failed: $e');
      rethrow;
    }
  }

  /// 解压GZIP文件
  ///
  /// [gzipFilePath] gzip文件路径
  /// [outputDir] 输出目录，默认为临时目录
  Future<List<String>> extractGzip({
    required String gzipFilePath,
    String? outputDir,
  }) async {
    try {
      final gzipFile = File(gzipFilePath);
      if (!await gzipFile.exists()) {
        throw Exception('gzip文件不存在: $gzipFilePath');
      }

      // 读取gzip文件
      var gzipBytes = await gzipFile.readAsBytes();
      log(TAG, 'read gzip file size: ${gzipBytes.length} bytes');

      // 解压GZIP
      log(TAG, 'start to decompress gzip file...');
      final tarBytes = GZipDecoder().decodeBytes(gzipBytes);
      log(
        TAG,
        'gzip decompressed successfully, tar size: ${tarBytes.length} bytes',
      );

      // 解压TAR
      log(TAG, 'start to extract tar archive...');
      final archive = TarDecoder().decodeBytes(tarBytes);
      log(TAG, 'tar extracted successfully, contains ${archive.length} files');

      // 确定输出目录
      final outputDirectory = outputDir ?? (await getTemporaryDirectory()).path;
      final extractedFiles = <String>[];

      // 提取文件
      for (final file in archive) {
        if (file.isFile) {
          // 写入解压后的文件
          final outputPath = path.join(outputDirectory, file.name);
          final outputFile = File(outputPath);
          await outputFile.writeAsBytes(file.content);

          extractedFiles.add(outputPath);
          log(TAG, 'extract file: ${file.name} -> $outputPath');
        }
      }

      log(TAG, 'successfully extracted ${extractedFiles.length} files');
      return extractedFiles;
    } catch (e) {
      log(TAG, 'extract gzip file failed: $e');
      log(TAG, 'error type: ${e.runtimeType}');
      rethrow;
    }
  }

  /// 清除缓存的文件
  Future<void> clearCache() async {
    try {
      if (_lastGeneratedFilePath != null) {
        final file = File(_lastGeneratedFilePath!);
        if (await file.exists()) {
          await file.delete();
        }
      }
      _lastGeneratedFilePath = null;
      _lastGenerateTime = null;
    } catch (e) {
      log(TAG, 'clear cache failed: $e');
    }
  }

  /// 获取上次生成的文件路径
  String? get lastGeneratedFilePath => _lastGeneratedFilePath;

  /// 获取上次生成的时间
  DateTime? get lastGenerateTime => _lastGenerateTime;

  /// 检查是否在防重复点击时间窗口内
  bool get isInThrottleWindow => _shouldUseLastGeneratedFile();

  /// 获取文件大小信息
  Future<Map<String, dynamic>> getFileInfo(String filePath) async {
    try {
      final file = File(filePath);
      if (await file.exists()) {
        final stat = await file.stat();
        return {
          'path': filePath,
          'name': path.basename(filePath),
          'size': stat.size,
          'modified': stat.modified,
          'created': stat.changed,
        };
      }
      return {};
    } catch (e) {
      log(TAG, 'get file info failed: $e');
      return {};
    }
  }

  /// 批量获取文件信息
  Future<List<Map<String, dynamic>>> getFilesInfo(
    List<String> filePaths,
  ) async {
    final filesInfo = <Map<String, dynamic>>[];

    for (final filePath in filePaths) {
      final info = await getFileInfo(filePath);
      if (info.isNotEmpty) {
        filesInfo.add(info);
      }
    }

    return filesInfo;
  }

  /// 检查文件是否为GZIP格式
  Future<bool> isGzipFile(String filePath) async {
    try {
      final file = File(filePath);
      if (!await file.exists()) {
        return false;
      }

      // 读取文件头部字节
      final bytes = await file.openRead(0, 2).first;
      if (bytes.length < 2) {
        return false;
      }

      // GZIP文件头标识：0x1f 0x8b
      return bytes[0] == 0x1f && bytes[1] == 0x8b;
    } catch (e) {
      log(TAG, 'check gzip file format failed: $e');
      return false;
    }
  }

  /// 获取GZIP文件中的文件列表
  Future<List<String>> getGzipFileList(String gzipFilePath) async {
    try {
      final gzipFile = File(gzipFilePath);
      if (!await gzipFile.exists()) {
        throw Exception('gzip file not exists: $gzipFilePath');
      }

      // 读取并解压GZIP
      var gzipBytes = await gzipFile.readAsBytes();
      final tarBytes = GZipDecoder().decodeBytes(gzipBytes);

      // 解析TAR归档
      final archive = TarDecoder().decodeBytes(tarBytes);

      final fileList = <String>[];
      for (final file in archive) {
        if (file.isFile) {
          fileList.add(file.name);
        }
      }

      return fileList;
    } catch (e) {
      log(TAG, 'get gzip file list failed: $e');
      rethrow;
    }
  }

  /// 获取压缩比信息
  Future<Map<String, dynamic>> getCompressionInfo(String gzipFilePath) async {
    try {
      final gzipFile = File(gzipFilePath);
      if (!await gzipFile.exists()) {
        return {};
      }

      final gzipSize = await gzipFile.length();

      // 解压获取原始大小
      var gzipBytes = await gzipFile.readAsBytes();
      final tarBytes = GZipDecoder().decodeBytes(gzipBytes);
      final originalSize = tarBytes.length;

      final compressionRatio = originalSize > 0
          ? ((1 - gzipSize / originalSize) * 100).toStringAsFixed(2)
          : '0.00';

      return {
        'originalSize': originalSize,
        'compressedSize': gzipSize,
        'compressionRatio': '$compressionRatio%',
        'spaceSaved': originalSize - gzipSize,
      };
    } catch (e) {
      log(TAG, 'get compression info failed: $e');
      return {};
    }
  }
}
