/*
 * Copyright 2023 Hongen Wang All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';

import 'package:date_format/date_format.dart';
import 'package:proxypin/network/bin/configuration.dart';
import 'package:proxypin/network/http/http.dart';
import 'package:proxypin/network/util/logger.dart';
import 'package:proxypin/storage/path.dart';
import 'package:proxypin/utils/files.dart';
import 'package:proxypin/utils/har.dart';
import 'package:proxypin/utils/listenable_list.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';

///历史存储
///@Author WangHongEn
class HistoryStorage {
  static HistoryStorage? _instance;
  final File _storageFile;
  static final StreamController<HistoryItem> _remoteImportedController = StreamController<HistoryItem>.broadcast();

  HistoryStorage._internal(this._storageFile);

  static final ListenableList<HistoryItem> _histories = ListenableList();

  ///单例
  static Future<HistoryStorage> get instance async {
    if (_instance == null) {
      var file = await Paths.getPath("histories.json");
      _instance = HistoryStorage._internal(file);
      await _instance!._init();
    }
    return _instance!;
  }

  //初始化
  Future<void> _init() async {
    if (await _storageFile.exists()) {
      var content = await _storageFile.readAsString();
      if (content.trim().isEmpty) {
        return;
      }
      try {
        var list = jsonDecode(content) as List<dynamic>;
        for (var entry in list) {
          _histories.add(HistoryItem.formJson(entry));
        }
      } catch (e) {
        logger.e("历史记录解析错误", error: e);
      }
    }
  }

  static Future<String> _homePath() async {
    final home = await getApplicationSupportDirectory();
    return '${home.path}${Platform.pathSeparator}history';
  }

  /// 获取历史记录
  List<HistoryItem> get histories {
    return _histories.source;
  }

  static Stream<HistoryItem> get onRemoteImported => _remoteImportedController.stream;

  static const String remoteHistoryPrefix = '[Remote] ';

  void addListener(ListenerListEvent<HistoryItem> listener) {
    _histories.addListener(listener);
  }

  void removeListener(ListenerListEvent<HistoryItem> listener) {
    _histories.removeListener(listener);
  }

  ///打开文件
  static Future<File> openFile(String name) async {
    final homePath = await _homePath();
    var file = File('$homePath${Platform.pathSeparator}$name');
    return file.create(recursive: true);
  }

  /// 添加历史记录
  Future<HistoryItem> addHistory(String name, File file, int requestLength) async {
    var historyItem = HistoryItem(name, file.path, requestLength, await file.length());
    _histories.add(historyItem);
    refresh();
    return historyItem;
  }

  int getIndex(HistoryItem item) {
    return _histories.indexOf(item);
  }

  //更新
  Future<void> updateHistory(int index, HistoryItem item) async {
    _histories.update(index, item);
    refresh();
  }

  //获取
  HistoryItem getHistory(int index) {
    return _histories.source[index];
  }

  Future<void> refresh() async {
    await _storageFile.writeAsString(jsonEncode(_histories.source));
  }

  ///删除
  Future<void> removeHistory(int index) async {
    var history = _histories.removeAt(index);
    logger.i('删除历史记录 $history');
    final homePath = await _homePath();
    var file = File('$homePath${Platform.pathSeparator}${Files.getName(history.path)}');
    file.delete();
    await refresh();
  }

  //获取请求列表
  Future<List<HttpRequest>> getRequests(HistoryItem history) async {
    if (history.requests == null) {
      final homePath = await _homePath();
      String path = '$homePath${Platform.pathSeparator}${Files.getName(history.path)}';
      var file = File(path);
      history.requests = await Har.readFile(file);
      history.requestLength = history.requests!.length;
      file.length().then((size) => history.fileSize = size);
    }

    return history.requests!;
  }

  ///刷新requests
  Future<void> flushRequests(HistoryItem history, List<HttpRequest> requests) async {
    logger.i("刷新历史记录 $history");
    final homePath = await _homePath();
    String path = '$homePath${Platform.pathSeparator}${Files.getName(history.path)}';
    var file = File(path);
    for (int i = 0; i < requests.length; i++) {
      var request = requests[i];
      var har = Har.toHar(request);
      await file.writeAsString("${jsonEncode(har)},\n", mode: i == 0 ? FileMode.write : FileMode.append);
    }

    history.requestLength = requests.length;
    await file.length().then((size) => history.fileSize = size);
    await refresh();
  }

  Future<HistoryItem> addRequests(Iterable<HttpRequest> requests,
      {String? name, bool notifyRemoteImported = false}) async {
    final list = requests.toList();
    final historyFile = await HistoryStorage.openFile("${DateTime.now().millisecondsSinceEpoch}.txt");
    final open = await historyFile.open(mode: FileMode.append);
    try {
      for (var request in list) {
        await open.writeString(jsonEncode(Har.toHar(request)));
        await open.writeString(",\n");
      }
    } finally {
      await open.close();
    }

    var historyName = (name == null || name.trim().isEmpty)
        ? formatDate(DateTime.now(), [mm, '-', d, ' ', HH, ':', nn, ':', ss])
        : name;
    if (notifyRemoteImported) {
      final hasRemotePrefix = historyName.startsWith(remoteHistoryPrefix) || historyName.startsWith('【远程】');
      if (!hasRemotePrefix) {
        historyName = '$remoteHistoryPrefix$historyName';
      }
    }
    final historyItem = await addHistory(historyName, historyFile, list.length);
    if (notifyRemoteImported) {
      _remoteImportedController.add(historyItem);
    }
    return historyItem;
  }

  //添加历史
  Future<HistoryItem> addHarFile(XFile file) async {
    var readAsBytes = await file.readAsString();
    var json = jsonDecode(readAsBytes);
    var log = json['log'];
    String name = formatDate(DateTime.now(), [mm, '-', d, ' ', HH, ':', nn, ':', ss]);
    List? pages = log['pages'] as List?;
    if (pages?.isNotEmpty == true) {
      name = pages?.first['title'];
    }

    //解析请求
    List entries = log['entries'];
    var list = entries.map((e) => Har.toRequest(e)).toList();

    return addRequests(list, name: name);
  }
}

class HistoryTask extends ListenerListEvent<HttpRequest> {
  HistoryItem? history;
  Timer? timer;
  final Queue writeList = Queue();

  RandomAccessFile? open;
  bool locked = false;

  static HistoryTask? _instance;

  final Configuration configuration;
  final ListenableList<HttpRequest> sourceList;

  HistoryTask(this.configuration, this.sourceList) {
    logger.d("start history task");
    if (configuration.historyCacheTime != 0) {
      sourceList.addListener(this);
      Future.delayed(const Duration(seconds: 3), () => cleanHistory());
    }
  }

  static HistoryTask ensureInstance(Configuration configuration, ListenableList<HttpRequest> sourceList) {
    return _instance ??= HistoryTask(configuration, sourceList);
  }

  //清理历史数据
  Future<void> cleanHistory() async {
    if (configuration.historyCacheTime == 0) {
      return;
    }
    var overdueTime = DateTime.now().subtract(Duration(days: configuration.historyCacheTime));
    var historyStorage = await HistoryStorage.instance;
    var histories = historyStorage.histories;
    for (int i = 0; i < histories.length; i++) {
      if (histories.elementAt(i).createTime.isBefore(overdueTime)) {
        await historyStorage.removeHistory(i);
        i--;
      }
    }
  }

  @override
  void onAdd(HttpRequest item) {
    if (history == null) {
      startTask();
      return;
    }
    writeList.add(item);
  }

  @override
  void onRemove(HttpRequest item) => resetList();

  @override
  void onBatchRemove(List<HttpRequest> items) => resetList();

  @override
  void clear(List<HttpRequest> items) => resetList();

  Future<void> resetList() async {
    locked = true;
    await open?.lock().timeout(Duration(seconds: 3), onTimeout: () => open!.unlock());
    open = await open?.truncate(0);
    await open?.setPosition(0);
    history?.requestLength = 0;
    history?.requests = null;
    writeList.clear();
    writeList.addAll(sourceList.source);
    locked = false;
    open?.unlock();
  }

  void cancelTask() {
    timer?.cancel();
    timer = null;
    open?.close();
    open = null;
    history = null;
    sourceList.removeListener(this);
    writeList.clear();
  }

  //写入任务
  Future<void> startTask() async {
    if (history != null || locked) return;
    locked = true;

    HistoryStorage storage = await HistoryStorage.instance;
    var name = formatDate(DateTime.now(), [mm, '-', d, ' ', HH, ':', nn, ':', ss]);
    File file = await HistoryStorage.openFile("${DateTime.now().millisecondsSinceEpoch}.txt");
    history = await storage.addHistory(name, file, 0);
    writeList.clear();
    writeList.addAll(sourceList.source);
    locked = false;

    open = await file.open(mode: FileMode.append);
    timer = Timer.periodic(const Duration(seconds: 5), (it) => writeTask());
  }

  //写入任务
  Future<void> writeTask() async {
    if (writeList.isEmpty) {
      return;
    }

    bool changed = false;
    while (writeList.isNotEmpty && !locked) {
      var request = writeList.removeFirst();
      var har = Har.toHar(request);

      await open?.writeString("${jsonEncode(har)},\n");

      history!.requestLength++;
      changed = true;
    }

    if (!changed) return;
    history!.fileSize = await open!.length();
    history!.requests = null;
    var historyStorage = await HistoryStorage.instance;
    historyStorage.updateHistory(historyStorage.getIndex(history!), history!);
  }
}

/// 历史记录
class HistoryItem {
  String name;
  final String path; // 文件路径
  int requestLength = 0; // 请求数量
  int? fileSize; // 文件大小
  DateTime createTime = DateTime.now();

  List<HttpRequest>? requests;

  HistoryItem(this.name, this.path, this.requestLength, this.fileSize, {DateTime? createTime})
      : createTime = createTime ?? DateTime.now();

  //json反序列化
  factory HistoryItem.formJson(Map<String, dynamic> map) {
    return HistoryItem(map['name'], map['path'], map['requestLength'], map['fileSize'],
        createTime: map['createTime'] == null ? null : DateTime.fromMillisecondsSinceEpoch(map['createTime']));
  }

  //json序列化
  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'path': path,
      'requestLength': requestLength,
      'fileSize': fileSize,
      'createTime': createTime.millisecondsSinceEpoch,
    };
  }

  //获取文件大小
  String get size {
    if (this.fileSize == null) {
      return "";
    }

    int fileSize = this.fileSize!;
    if (fileSize > 1024 * 1024) {
      return "${(fileSize / 1024 / 1024).toStringAsFixed(1)}MB";
    }

    return "${(fileSize / 1024).toStringAsFixed(1)}KB";
  }

  @override
  String toString() {
    return "$path $requestLength $fileSize";
  }
}
