import 'dart:io';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
import 'models/version_info.dart';

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

/// 下载进度
class DownloadProgress {
  final int downloaded;
  final int total;
  final DownloadStatus status;
  final String? error;

  const DownloadProgress({
    required this.downloaded,
    required this.total,
    required this.status,
    this.error,
  });

  double get progress => total > 0 ? downloaded / total : 0;
  String get progressText => '${(progress * 100).toStringAsFixed(1)}%';

  factory DownloadProgress.initial() {
    return const DownloadProgress(
      downloaded: 0,
      total: 0,
      status: DownloadStatus.idle,
    );
  }

  DownloadProgress copyWith({
    int? downloaded,
    int? total,
    DownloadStatus? status,
    String? error,
  }) {
    return DownloadProgress(
      downloaded: downloaded ?? this.downloaded,
      total: total ?? this.total,
      status: status ?? this.status,
      error: error ?? this.error,
    );
  }
}

/// 下载管理器
/// 负责下载 APK 文件，支持断点续传
class DownloadManager {
  final VersionInfo versionInfo;
  final _progressController = StreamController<DownloadProgress>.broadcast();

  http.Client? _httpClient;
  File? _downloadFile;
  bool _isCancelled = false;

  DownloadManager({required this.versionInfo});

  Stream<DownloadProgress> get progressStream => _progressController.stream;

  /// 获取下载目录
  Future<Directory> _getDownloadDir() async {
    final appDir = await getApplicationDocumentsDirectory();
    final downloadDir = Directory('${appDir.path}/updates');
    if (!await downloadDir.exists()) {
      await downloadDir.create(recursive: true);
    }
    return downloadDir;
  }

  /// 获取下载文件路径
  Future<File> _getDownloadFile() async {
    final downloadDir = await _getDownloadDir();
    final fileName = 'mining_app_${versionInfo.versionName}.apk';
    return File('${downloadDir.path}/$fileName');
  }

  /// 开始下载
  Future<File?> startDownload() async {
    _isCancelled = false;
    _httpClient = http.Client();

    try {
      _downloadFile = await _getDownloadFile();

      // 检查是否有已下载的部分
      int downloadedBytes = 0;
      if (await _downloadFile!.exists()) {
        downloadedBytes = await _downloadFile!.length();

        // 如果已经完整下载，验证文件
        if (downloadedBytes == versionInfo.fileSize) {
          _progressController.add(DownloadProgress(
            downloaded: downloadedBytes,
            total: versionInfo.fileSize,
            status: DownloadStatus.verifying,
          ));

          if (await _verifyFile(_downloadFile!)) {
            _progressController.add(DownloadProgress(
              downloaded: downloadedBytes,
              total: versionInfo.fileSize,
              status: DownloadStatus.completed,
            ));
            return _downloadFile;
          } else {
            // 文件损坏，删除重新下载
            await _downloadFile!.delete();
            downloadedBytes = 0;
          }
        }
      }

      debugPrint('Starting download from byte $downloadedBytes');
      _progressController.add(DownloadProgress(
        downloaded: downloadedBytes,
        total: versionInfo.fileSize,
        status: DownloadStatus.downloading,
      ));

      // 创建请求，支持断点续传
      final request = http.Request('GET', Uri.parse(versionInfo.downloadUrl));
      if (downloadedBytes > 0) {
        request.headers['Range'] = 'bytes=$downloadedBytes-';
      }

      final response = await _httpClient!.send(request);

      if (response.statusCode != 200 && response.statusCode != 206) {
        throw Exception('Download failed: HTTP ${response.statusCode}');
      }

      // 打开文件进行写入
      final fileSink = _downloadFile!.openWrite(mode: FileMode.append);

      try {
        await for (final chunk in response.stream) {
          if (_isCancelled) {
            throw Exception('Download cancelled');
          }

          fileSink.add(chunk);
          downloadedBytes += chunk.length;

          _progressController.add(DownloadProgress(
            downloaded: downloadedBytes,
            total: versionInfo.fileSize,
            status: DownloadStatus.downloading,
          ));
        }
      } finally {
        await fileSink.close();
      }

      // 验证文件
      _progressController.add(DownloadProgress(
        downloaded: downloadedBytes,
        total: versionInfo.fileSize,
        status: DownloadStatus.verifying,
      ));

      if (await _verifyFile(_downloadFile!)) {
        _progressController.add(DownloadProgress(
          downloaded: downloadedBytes,
          total: versionInfo.fileSize,
          status: DownloadStatus.completed,
        ));
        return _downloadFile;
      } else {
        await _downloadFile!.delete();
        throw Exception('File verification failed');
      }
    } catch (e) {
      debugPrint('Download error: $e');
      _progressController.add(DownloadProgress(
        downloaded: 0,
        total: versionInfo.fileSize,
        status: DownloadStatus.failed,
        error: e.toString(),
      ));
      return null;
    } finally {
      _httpClient?.close();
      _httpClient = null;
    }
  }

  /// 取消下载
  void cancelDownload() {
    _isCancelled = true;
    _httpClient?.close();
  }

  /// 验证文件 SHA256
  Future<bool> _verifyFile(File file) async {
    try {
      final bytes = await file.readAsBytes();
      final digest = sha256.convert(bytes);
      final actualHash = digest.toString();

      debugPrint('Expected hash: ${versionInfo.fileSha256}');
      debugPrint('Actual hash: $actualHash');

      return actualHash.toLowerCase() == versionInfo.fileSha256.toLowerCase();
    } catch (e) {
      debugPrint('Verify error: $e');
      return false;
    }
  }

  /// 清理下载文件
  Future<void> cleanup() async {
    try {
      if (_downloadFile != null && await _downloadFile!.exists()) {
        await _downloadFile!.delete();
      }
    } catch (e) {
      debugPrint('Cleanup error: $e');
    }
  }

  void dispose() {
    cancelDownload();
    _progressController.close();
  }
}
