import 'dart:async';
import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:logging/logging.dart';
import 'package:puzzleweave/models/api_helper.dart';
import 'package:puzzleweave/models/items.dart';
import 'package:puzzleweave/utils/utils.dart';
import 'package:puzzleweave/utils/image_decoder.dart';

import 'data.dart';

final Logger _log = Logger('download.dart');

/// ✅ 优化：增加缓存数量到3个
const maxCachedItems = 3;

/// Singleton
class Download {
  static final Download _instance = Download._internal();
  factory Download() => _instance;
  Download._internal();

  final Map<String, DownloadItem> _cache = {};

  DownloadItem download(String url, String cachePath) {
    if (_cache[url] != null) {
      _log.info('Cache hit for $url');
      _cache[url]!.touch();
      return _cache[url]!;
    } else {
      final item = DownloadItem(url, cachePath);
      _cache[url] = item;
      _watch(item);
      return item;
    }
  }

  _watch(DownloadItem item) async {
    try {
      await item.loadCompleter.future;
      Future.microtask(_clean);
    } catch (err) {
      _log.info('Watch download item got error: $err');
      _cache.remove(item.url);
    }
  }

  _clean() {
    final list = _cache.values.where((item) => item.loadCompleter.isCompleted).toList();
    if (list.length <= maxCachedItems) return;
    _log.info('Cleaning download cache...');

    list.sort((a, b) => a.lastUsed.compareTo(b.lastUsed));
    while (list.length > maxCachedItems) {
      final item = list.removeAt(0);
      _log.info('Cleaning item from memory: $item');
      item.dispose();
      _cache.remove(item.url);
    }
  }

  /// ✅ 新增：清理所有内存中的数据（保留下载任务）
  void clearAllData() {
    _log.info('Clearing all download data from memory');
    int count = 0;
    _cache.forEach((key, item) {
      if (item.data != null) {
        item.clearData();
        count++;
      }
    });
    _log.info('Cleared $count download items from memory');
  }

  /// ✅ 新增：获取当前缓存统计
  Map<String, dynamic> getCacheStats() {
    final total = _cache.length;
    final completed = _cache.values.where((item) => item.loadCompleter.isCompleted).length;
    final withData = _cache.values.where((item) => item.data != null).length;
    return {'total': total, 'completed': completed, 'withData': withData};
  }

  clearAllCached() async {
    final file = await localFile('cache');
    await file.delete(recursive: true);
  }
}

class DownloadItem {
  final String url;
  final String cachePath;
  ValueNotifier<double> progress = ValueNotifier(0.0);
  final Completer<void> loadCompleter = Completer();
  Client? client;
  StreamSubscription? subscription;

  /// ✅ 新增：最后访问时间
  DateTime? _lastAccessTime;

  /// ✅ 新增：数据保留时长（5分钟未使用则自动清理）
  static const _dataRetentionDuration = Duration(minutes: 5);

  /// ✅ 新增：自动清理定时器
  Timer? _cleanupTimer;

  int lastUsed;
  Uint8List? _data;
  bool _isDisposed = false;

  DownloadItem(this.url, this.cachePath) : lastUsed = DateTime.now().millisecondsSinceEpoch {
    _log.info('New download item for: $url');
    _start();
  }

  Uint8List? get data => _data;
  set data(Uint8List? val) => _data = val;

  touch() {
    lastUsed = DateTime.now().millisecondsSinceEpoch;
    _lastAccessTime = DateTime.now();
  }

  _start() async {
    try {
      await _download();
      if (!loadCompleter.isCompleted) loadCompleter.complete();
    } catch (err) {
      if (!loadCompleter.isCompleted) loadCompleter.completeError(err);
    }
  }

  Future<void> _download() async {
    progress.value = 0;
    final file = await localFile(cachePath);
    _checkDispose();

    if (await file.exists()) {
      _log.info('Disk cache hit (Metadata only) for $cachePath');
      progress.value = 1.0;
      return;
    }

    final List<int> bytes = [];
    client = Client();
    final response = await client!.send(Request('GET', Uri.parse(url)));
    _checkDispose();

    if (response.statusCode != 200) throw Exception('Status:${response.statusCode}');
    final length = response.contentLength ?? 0;

    final streamCompleter = Completer();
    subscription = response.stream.listen(
      (value) {
        bytes.addAll(value);
        if (length > 0) progress.value = bytes.length / length;
      },
      onDone: () => streamCompleter.complete(),
      onError: (e) => streamCompleter.completeError(e),
      cancelOnError: true,
    );

    await streamCompleter.future;
    _checkDispose();

    if (bytes.length < 1024 + 24) {
      throw Exception('Downloaded data is too small (${bytes.length} bytes)');
    }

    final cleanBytes = Uint8List.fromList(bytes.sublist(24));
    _data = cleanBytes;

    /// ✅ 原子性写入优化
    final tempPath = '$cachePath.tmp';
    await saveBytes(tempPath, _data!);
    final tempFile = await localFile(tempPath);
    final finalFile = await localFile(cachePath);
    await tempFile.rename(finalFile.path);

    _log.info('Download and save complete for $url');
    client?.close();

    /// ✅ 调度自动清理
    _scheduleCleanup();
  }

  /// ✅ 供 Loader 真正需要数据时调用
  Future<Uint8List> ensureDataLoaded() async {
    _lastAccessTime = DateTime.now();

    if (_data != null) {
      _log.info('Data cache hit for $cachePath');
      _scheduleCleanup();
      return _data!;
    }

    _log.info('Performing late read from disk: $cachePath');
    final file = await localFile(cachePath);
    _data = await file.readAsBytes();
    _scheduleCleanup();
    return _data!;
  }

  /// ✅ 新增：调度自动清理
  void _scheduleCleanup() {
    _cleanupTimer?.cancel();
    _cleanupTimer = Timer(_dataRetentionDuration, () {
      if (_data != null && _lastAccessTime != null) {
        final timeSinceLastAccess = DateTime.now().difference(_lastAccessTime!);
        if (timeSinceLastAccess >= _dataRetentionDuration) {
          _log.info('Auto-cleaning unused data: $cachePath');
          _data = null;
        }
      }
    });
  }

  /// ✅ 新增：手动清理数据
  void clearData() {
    _cleanupTimer?.cancel();
    _data = null;
    _log.info('Manually cleared data: $cachePath');
  }

  _checkDispose() {
    if (_isDisposed) throw Exception('Disposed');
  }

  dispose() {
    _isDisposed = true;
    _cleanupTimer?.cancel();
    _data = null;
    subscription?.cancel();
    client?.close();
  }

  @override
  String toString() => '[$cachePath]';
}

abstract class ItemLoader {
  final Completer completer = Completer();
  Uint8List? get data;
  ValueNotifier<double> get progress;

  ItemLoader();

  Future<Uint8List> _prepareData() async {
    await completer.future;
    if (data == null) throw 'Data missing after completion';
    return data!;
  }

  Future<ui.Image> getImageBySize(int width, int height, {allowUpscaling = false}) async {
    final bytes = await _prepareData();
    final codec = await ui.instantiateImageCodec(bytes, targetHeight: height, targetWidth: width, allowUpscaling: allowUpscaling);
    return (await codec.getNextFrame()).image;
  }

  Future<ui.Image> getImage() async {
    final bytes = await _prepareData();
    final codec = await ui.instantiateImageCodec(bytes);
    return (await codec.getNextFrame()).image;
  }

  static void preload(ListItem item, String quality) {
    if (BuiltinRegistry.contains(item.id)) {
      _log.info('Preload: Skipping builtin ${item.id}');
      return;
    }
    if (item is RemoteItem) {
      Download().download(ApiHelper.imageUri(item.id, quality), item.cachePath);
    }
  }

  factory ItemLoader.load(ListItem item, String quality) {
    if (BuiltinRegistry.contains(item.id)) {
      _log.info('Built-in hit: ${item.id}, skip network.');
      return AssetItemLoader('assets/builtin/${item.id}.jpeg');
    }
    switch (item.runtimeType) {
      case RemoteItem:
        return RemoteItemLoader(ApiHelper.imageUri(item.id, quality), item.cachePath);
      case AssetItem:
        return AssetItemLoader((item as AssetItem).image);
      default:
        throw 'Unknown item type';
    }
  }
}

class AssetItemLoader extends ItemLoader {
  final String path;
  Uint8List? _data;

  @override
  ValueNotifier<double> progress = ValueNotifier(0);

  AssetItemLoader(this.path) {
    _load();
  }

  _load() async {
    try {
      _data = await loadFileDataFromAsset(path);
      completer.complete();
      progress.value = 1.0;
    } catch (e) {
      completer.completeError(e);
    }
  }

  @override
  Uint8List? get data => _data;
}

class RemoteItemLoader extends ItemLoader {
  final String url;
  final String cachePath;
  late final DownloadItem downloadItem;

  RemoteItemLoader(this.url, this.cachePath) {
    downloadItem = Download().download(url, cachePath);
    _load();
  }

  _load() async {
    try {
      await downloadItem.loadCompleter.future;
      completer.complete();
    } catch (err) {
      completer.completeError(err);
    }
  }

  @override
  Uint8List? get data => downloadItem.data;

  @override
  Future<Uint8List> _prepareData() async {
    await completer.future;
    return await downloadItem.ensureDataLoaded();
  }

  @override
  ValueNotifier<double> get progress => downloadItem.progress;
}
