import 'dart:io';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/foundation.dart';

/// 下载状态
enum DownloadStatus {
  idle,
  downloading,
  verifying,
  completed,
  failed,
  cancelled,
}

/// 下载进度回调
typedef DownloadProgressCallback = void Function(int received, int total);

/// 下载管理器
/// 负责下载 APK 文件并验证完整性，支持断点续传
class DownloadManager {
  final Dio _dio = Dio();
  CancelToken? _cancelToken;
  DownloadStatus _status = DownloadStatus.idle;

  DownloadStatus get status => _status;

  /// 下载 APK 文件（支持断点续传）
  Future<File?> downloadApk({
    required String url,
    required String sha256Expected,
    DownloadProgressCallback? onProgress,
  }) async {
    try {
      if (!url.startsWith('https://')) {
        debugPrint('Download URL must use HTTPS');
        _status = DownloadStatus.failed;
        return null;
      }

      _status = DownloadStatus.downloading;
      _cancelToken = CancelToken();

      final dir = await getExternalStorageDirectory() ?? await getApplicationSupportDirectory();
      final savePath = '${dir.path}/app_update.apk';
      final tempPath = '${dir.path}/app_update.apk.tmp';
      final file = File(savePath);
      final tempFile = File(tempPath);

      int downloadedBytes = 0;
      if (await tempFile.exists()) {
        downloadedBytes = await tempFile.length();
      }

      if (await file.exists()) {
        await file.delete();
      }

      final response = await _dio.get<ResponseBody>(
        url,
        cancelToken: _cancelToken,
        options: Options(
          responseType: ResponseType.stream,
          receiveTimeout: const Duration(minutes: 10),
          sendTimeout: const Duration(minutes: 10),
          headers: downloadedBytes > 0
              ? {'Range': 'bytes=$downloadedBytes-'}
              : null,
        ),
      );

      int totalBytes = 0;
      final contentLength = response.headers.value('content-length');
      final contentRange = response.headers.value('content-range');

      if (contentRange != null) {
        final match = RegExp(r'bytes \d+-\d+/(\d+)').firstMatch(contentRange);
        if (match != null) {
          totalBytes = int.parse(match.group(1)!);
        }
      } else if (contentLength != null) {
        totalBytes = int.parse(contentLength) + downloadedBytes;
      }

      final sink = tempFile.openWrite(mode: FileMode.append);
      int receivedBytes = downloadedBytes;

      try {
        await for (final chunk in response.data!.stream) {
          sink.add(chunk);
          receivedBytes += chunk.length;

          if (totalBytes > 0) {
            onProgress?.call(receivedBytes, totalBytes);
          }
        }
        await sink.flush();
      } finally {
        await sink.close();
      }

      await tempFile.rename(savePath);

      _status = DownloadStatus.verifying;

      final isValid = await _verifySha256(file, sha256Expected);
      if (!isValid) {
        debugPrint('SHA-256 verification failed');
        await file.delete();
        _status = DownloadStatus.failed;
        return null;
      }

      _status = DownloadStatus.completed;
      return file;
    } on DioException catch (e) {
      if (e.type == DioExceptionType.cancel) {
        _status = DownloadStatus.cancelled;
      } else {
        _status = DownloadStatus.failed;
      }
      return null;
    } catch (e) {
      debugPrint('Download failed: $e');
      _status = DownloadStatus.failed;
      return null;
    }
  }

  void cancelDownload() {
    _cancelToken?.cancel('User cancelled download');
    _status = DownloadStatus.cancelled;
  }

  void reset() {
    _cancelToken = null;
    _status = DownloadStatus.idle;
  }

  Future<bool> _verifySha256(File file, String expectedSha256) async {
    try {
      final bytes = await file.readAsBytes();
      final digest = sha256.convert(bytes);
      final actualSha256 = digest.toString();
      return actualSha256.toLowerCase() == expectedSha256.toLowerCase();
    } catch (e) {
      debugPrint('SHA-256 verification error: $e');
      return false;
    }
  }

  Future<void> cleanupDownloadedApk() async {
    try {
      final dir = await getExternalStorageDirectory() ?? await getApplicationSupportDirectory();
      final file = File('${dir.path}/app_update.apk');
      final tempFile = File('${dir.path}/app_update.apk.tmp');

      if (await file.exists()) await file.delete();
      if (await tempFile.exists()) await tempFile.delete();
    } catch (e) {
      debugPrint('Cleanup failed: $e');
    }
  }
}
