import 'dart:async';
import 'dart:io';
import 'dart:isolate';

import 'package:flutter/material.dart';

import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:super_sliver_list/super_sliver_list.dart';

import 'package:namida/base/pull_to_refresh.dart';
import 'package:namida/class/file_parts.dart';
import 'package:namida/controller/directory_index.dart';
import 'package:namida/controller/navigator_controller.dart';
import 'package:namida/controller/platform/namida_storage/namida_storage.dart';
import 'package:namida/controller/settings_controller.dart';
import 'package:namida/core/constants.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/core/icon_fonts/broken_icons.dart';
import 'package:namida/core/namida_converter_ext.dart';
import 'package:namida/core/translations/language.dart';
import 'package:namida/core/utils.dart';
import 'package:namida/main.dart';
import 'package:namida/packages/three_arched_circle.dart';
import 'package:namida/ui/dialogs/edit_tags_dialog.dart';
import 'package:namida/ui/widgets/artwork.dart';
import 'package:namida/ui/widgets/custom_widgets.dart';
import 'package:namida/ui/widgets/expandable_box.dart';

enum FileBrowserSortType {
  name,
  dateModified,
  type, // extension
  size,
}

const _defaultMemeType = NamidaStorageFileMemeType.any;

class NamidaFileBrowser {
  static Future<File?> pickFile({
    String note = '',
    NamidaStorageFileMemeType memeType = _defaultMemeType,
    String? initialDirectory,
    NamidaFileExtensionsWrapper? allowedExtensions,
  }) async {
    return _NamidaFileBrowserBase.pickFile(
      note: note,
      allowedExtensions: allowedExtensions,
      memeType: memeType,
      initialDirectory: initialDirectory,
      onNavigate: _onNavigate,
      onPop: _onPop,
    );
  }

  static Future<List<File>> pickFiles({
    String note = '',
    NamidaStorageFileMemeType memeType = _defaultMemeType,
    String? initialDirectory,
    NamidaFileExtensionsWrapper? allowedExtensions,
  }) async {
    return _NamidaFileBrowserBase.pickFiles(
      note: note,
      allowedExtensions: allowedExtensions,
      memeType: memeType,
      initialDirectory: initialDirectory,
      onNavigate: _onNavigate,
      onPop: _onPop,
    );
  }

  static Future<Directory?> pickDirectory({
    String note = '',
    String? initialDirectory,
  }) async {
    return _NamidaFileBrowserBase.pickDirectory(
      note: note,
      initialDirectory: initialDirectory,
      onNavigate: _onNavigate,
      onPop: _onPop,
    );
  }

  static Future<List<Directory>> pickDirectories({
    String note = '',
    String? initialDirectory,
  }) async {
    return _NamidaFileBrowserBase.pickDirectories(
      note: note,
      initialDirectory: initialDirectory,
      onNavigate: _onNavigate,
      onPop: _onPop,
    );
  }

  static Future<String?> getDirectory({String note = ''}) async {
    return pickDirectory(note: note).then((value) => value?.path);
  }

  static Future<List<String>> getDirectories({String note = ''}) async {
    return pickDirectories(note: note).then((value) => value.map((e) => e.path).toList());
  }

  static void _onNavigate(_NamidaFileBrowserBase widget) {
    NamidaNavigator.inst.navigateToRoot(
      widget,
      transition: Transition.native,
    );
  }

  static void _onPop() {
    NamidaNavigator.inst.popRoot();
  }
}

typedef _NamidaFileBrowserNavigationCallback = void Function(_NamidaFileBrowserBase options);
typedef _NamidaFileBrowserPopCallback = void Function();

class _NamidaFileBrowserBase<T extends FileSystemEntity> extends StatefulWidget {
  final String note;
  final String? initialDirectory;
  final Completer<List<T>> onSelect;
  final NamidaFileExtensionsWrapper? allowedExtensions;
  final NamidaStorageFileMemeType memeType;
  final bool allowMultiple;
  final _NamidaFileBrowserPopCallback onPop;

  const _NamidaFileBrowserBase({
    super.key,
    required this.note,
    this.initialDirectory,
    required this.onSelect,
    this.allowedExtensions,
    this.memeType = _defaultMemeType,
    required this.allowMultiple,
    required this.onPop,
  });

  static Future<File?> pickFile({
    String note = '',
    NamidaFileExtensionsWrapper? allowedExtensions,
    NamidaStorageFileMemeType memeType = _defaultMemeType,
    String? initialDirectory,
    required _NamidaFileBrowserNavigationCallback onNavigate,
    required _NamidaFileBrowserPopCallback onPop,
  }) async {
    final completer = Completer<List<File>>();
    onNavigate(
      _NamidaFileBrowserBase<File>(
        note: note,
        initialDirectory: initialDirectory,
        allowedExtensions: allowedExtensions,
        memeType: memeType,
        onSelect: completer,
        allowMultiple: false,
        onPop: onPop,
      ),
    );
    final all = await completer.future;
    return all.firstOrNull;
  }

  static Future<List<File>> pickFiles({
    String note = '',
    NamidaFileExtensionsWrapper? allowedExtensions,
    NamidaStorageFileMemeType memeType = _defaultMemeType,
    String? initialDirectory,
    required _NamidaFileBrowserNavigationCallback onNavigate,
    required _NamidaFileBrowserPopCallback onPop,
  }) async {
    final completer = Completer<List<File>>();
    onNavigate(
      _NamidaFileBrowserBase<File>(
        note: note,
        initialDirectory: initialDirectory,
        allowedExtensions: allowedExtensions,
        memeType: memeType,
        onSelect: completer,
        allowMultiple: true,
        onPop: onPop,
      ),
    );
    return completer.future;
  }

  static Future<Directory?> pickDirectory({
    String note = '',
    String? initialDirectory,
    required _NamidaFileBrowserNavigationCallback onNavigate,
    required _NamidaFileBrowserPopCallback onPop,
  }) async {
    final completer = Completer<List<Directory>>();
    onNavigate(
      _NamidaFileBrowserBase<Directory>(
        note: note,
        initialDirectory: initialDirectory,
        onSelect: completer,
        allowMultiple: false,
        onPop: onPop,
      ),
    );
    final all = await completer.future;
    if (all.isEmpty) return null;
    return _fixSDCardDirectory(all[0]);
  }

  static Future<List<Directory>> pickDirectories({
    String note = '',
    String? initialDirectory,
    required _NamidaFileBrowserNavigationCallback onNavigate,
    required _NamidaFileBrowserPopCallback onPop,
  }) async {
    final completer = Completer<List<Directory>>();
    onNavigate(
      _NamidaFileBrowserBase<Directory>(
        note: note,
        initialDirectory: initialDirectory,
        onSelect: completer,
        allowMultiple: true,
        onPop: onPop,
      ),
    );
    final res = await completer.future;
    return res.map((e) => _fixSDCardDirectory(e)).toList();
  }

  static final _sdDirRegex = RegExp(r'/tree/(\w{4}-\w{4}):');
  static Directory _fixSDCardDirectory(Directory dir) {
    final replaced = dir.path.replaceFirstMapped(_sdDirRegex, (match) => '/storage/${match.group(1)}/');
    return Directory(replaced);
  }

  @override
  State<_NamidaFileBrowserBase> createState() => _NamidaFileBrowserState<T>();
}

class _NamidaFileBrowserState<T extends FileSystemEntity> extends State<_NamidaFileBrowserBase<T>> with TickerProviderStateMixin, PullToRefreshMixin {
  final _mainStoragePaths = <String>{};
  String _currentFolderPath = '';
  var _currentFiles = <File>[];
  var _currentFolders = <Directory>[];
  bool _isFetching = true;

  final _sortTypeToName = {
    FileBrowserSortType.name: lang.name,
    FileBrowserSortType.dateModified: lang.date,
    FileBrowserSortType.type: lang.extension,
    FileBrowserSortType.size: lang.size,
  };

  void _sortItems(FileBrowserSortType? type, bool? reversed, {bool refreshState = true}) {
    type ??= settings.fileBrowserSort.value;
    reversed ??= settings.fileBrowserSortReversed.value;

    void sorterFnFiles(Comparable<dynamic> Function(File item) fn) {
      reversed! ? _currentFiles.sortByReverse(fn) : _currentFiles.sortBy(fn);
    }

    void sorterFnFolder(Comparable<dynamic> Function(Directory item) fn) {
      reversed! ? _currentFolders.sortByReverse(fn) : _currentFolders.sortBy(fn);
    }

    switch (type) {
      case FileBrowserSortType.name:
        sorterFnFiles((item) => _pathToName(item.path).toLowerCase());
        sorterFnFolder((item) => _pathToName(item.path).toLowerCase());
      case FileBrowserSortType.dateModified:
        sorterFnFiles((item) => _currentInfoFiles[item.path]?.modified ?? DateTime(0));
        sorterFnFolder((item) => _currentInfoDirs[item.path]?.modified ?? DateTime(0));
      case FileBrowserSortType.type:
        sorterFnFiles((item) => _pathToExtension(item.path).toLowerCase());
        sorterFnFolder((item) => _pathToExtension(item.path).toLowerCase());
      case FileBrowserSortType.size:
        sorterFnFiles((item) => _currentInfoFiles[item.path]?.size ?? 0);
        sorterFnFolder((item) => _currentInfoDirs[item.path]?.size ?? 0);
    }

    if (type != settings.fileBrowserSort.value || reversed != settings.fileBrowserSortReversed.value) {
      settings.save(
        fileBrowserSort: type,
        fileBrowserSortReversed: reversed,
      );
    }

    if (refreshState) setState(() {});
  }

  final _showHiddenFiles = false.obs;
  bool _showEmptyFolders = false;

  static final _pathSeparator = Platform.pathSeparator;
  late final _scrollController = NamidaScrollController.create();
  late final _pathSplitsScrollController = NamidaScrollController.create();

  static String _pathToName(String path) {
    return path.pathReverseSplitter(_pathSeparator);
  }

  static String _pathToExtension(String path) {
    return path.pathReverseSplitter('.').toLowerCase();
  }

  Isolate? _isolate;
  ReceivePort? _resultPort;

  Isolate? _infoIsolate;
  ReceivePort? _infoPort;

  void _stopMainIsolates() {
    try {
      _resultPort?.close();
      _isolate?.kill(priority: Isolate.immediate);
      _isolate = null;
      _resultPort = null;
    } catch (_) {}
  }

  void _stopInfoIsolates() {
    try {
      _infoPort?.close();
      _infoIsolate?.kill(priority: Isolate.immediate);
      _infoIsolate = null;
      _infoPort = null;
    } catch (_) {}
  }

  Future<void> _fetchFiles(Directory dir, {bool clearPrevious = true}) async {
    final dirPath = dir.path;

    _currentFolderPath = dirPath;
    if (clearPrevious) {
      setState(() {
        _isFetching = true;
        _currentFiles = [];
        _currentFolders = [];
      });
    }

    (List<File>, List<Directory>, Object?) isolateRes = ([], [], null);
    try {
      _stopMainIsolates();
      _resultPort = ReceivePort();
      final params = (dir: dirPath, showHiddenFiles: _showHiddenFiles.value, allowedExtensionsWrappers: _effectiveAllowedExtensions, resultPort: _resultPort!.sendPort);
      _isolate = await Isolate.spawn(_fetchFilesIsolate, params);
      isolateRes = await _resultPort!.first as (List<File>, List<Directory>, Object?);
      // _fetchInfo(dirPath, isolateRes.$1, isolateRes.$2);

      _stopMainIsolates();
    } catch (e) {
      snackyy(title: lang.error, message: "$e", isError: true);
    }
    if (dirPath == _currentFolderPath) {
      setState(() {
        _isFetching = false;
        if (isolateRes.$1.isNotEmpty) _currentFiles = isolateRes.$1;
        if (isolateRes.$2.isNotEmpty) _currentFolders = isolateRes.$2;
        _sortItems(null, null, refreshState: false);
      });
      if (isolateRes.$3 != null) {
        snackyy(title: lang.error, message: isolateRes.$3!.toString(), isError: true);
      }
    }
  }

  var _currentInfoFiles = <String, NamidaFileStat>{};
  var _currentInfoDirs = <String, NamidaDirStat>{};
  bool _fetchingInfo = false;

  Future<void> _fetchInfo(Set<String> rootPaths) async {
    _fetchingInfo = true;
    try {
      _infoPort = ReceivePort();
      final params = (rootPaths, _infoPort!.sendPort);
      _infoIsolate = await Isolate.spawn(_fetchInfoIsolate, params);
      final res = await _infoPort!.first as (Map<String, NamidaFileStat>, Map<String, NamidaDirStat>);
      if (mounted) {
        setState(() {
          _currentInfoFiles = res.$1;
          _currentInfoDirs = res.$2;
          if (settings.fileBrowserSort.value != FileBrowserSortType.name) _sortItems(null, null, refreshState: false);
        });
      }
      _stopInfoIsolates();
    } catch (_) {}
    _fetchingInfo = false;
  }

  static void _fetchInfoIsolate((Set<String>, SendPort) params) {
    final infoFiles = <String, NamidaFileStat>{}; // <------
    final directoryForFiles = <String, List<String>>{}; // --^
    final directoryForFolders = <String, List<String>>{}; // --^
    final infoDirSize = <String, int>{};

    int onFileAdd(Directory dir, File f) {
      try {
        final stats = f.statSync();
        infoFiles[f.path] = NamidaFileStat(
          size: stats.size,
          accessed: stats.accessed,
          changed: stats.changed,
          modified: stats.modified,
        );
        directoryForFiles.addForce(dir.path, f.path);
        return stats.size;
      } catch (_) {
        return 0;
      }
    }

    void dirToParentsWalker(Directory dir, void Function(String parent) execute) {
      final dirPieces = dir.path.split(_pathSeparator);
      while (dirPieces.isNotEmpty) {
        final parentDirPath = dirPieces.join(_pathSeparator);
        execute(parentDirPath);
        dirPieces.removeLast();
      }
    }

    final infoDirs = <String, NamidaDirStat>{};
    void markDirAndParentAsInaccurate(Directory dir) {
      dirToParentsWalker(dir, (parent) {
        final currentInfo = infoDirs[parent];
        if (currentInfo?.accurate == false) return; // already marked
        infoDirs[parent] = currentInfo != null
            ? NamidaDirStat(
                accurate: false,
                filesCount: currentInfo.filesCount,
                foldersCount: currentInfo.foldersCount,
                size: currentInfo.size,
                accessed: currentInfo.accessed,
                changed: currentInfo.changed,
                modified: currentInfo.modified,
              )
            : NamidaDirStat(
                accurate: false,
                filesCount: 0,
                foldersCount: 0,
                size: 0,
                accessed: DateTime(0),
                changed: DateTime(0),
                modified: DateTime(0),
              );
      });
    }

    int dirSafeRecursiveListSync(Directory dir) {
      try {
        int totalSize = 0;
        final subDir = <Directory>[];
        final items = dir.listSync(recursive: false);
        for (var e in items) {
          if (e is File) {
            totalSize += onFileAdd(dir, e);
          } else if (e is Directory) {
            subDir.add(e);
            directoryForFolders.addForce(dir.path, e.path);
          }
        }
        for (var sub in subDir) {
          totalSize += dirSafeRecursiveListSync(
            sub,
          );
        }
        infoDirSize[dir.path] = totalSize;
        return totalSize;
      } catch (_) {
        markDirAndParentAsInaccurate(dir);
        return 0;
      }
    }

    for (final rootPath in params.$1) {
      dirSafeRecursiveListSync(Directory(rootPath));
    }

    void onDirInfo(MapEntry<String, List<String>> dirEntry, int size) {
      final dir = Directory(dirEntry.key);
      try {
        final dirStat = dir.statSync();
        infoDirs[dir.path] = NamidaDirStat(
          accurate: infoDirs[dir.path]?.accurate ?? true, // check if was marked innaccurate before.
          filesCount: directoryForFiles[dir.path]?.length ?? 0,
          foldersCount: directoryForFolders[dir.path]?.length ?? 0,
          size: size,
          accessed: dirStat.accessed,
          changed: dirStat.changed,
          modified: dirStat.modified,
        );
      } catch (_) {}
    }

    for (final fileEntry in directoryForFiles.entries) {
      int totalSize = 0;
      for (var e in fileEntry.value) {
        totalSize += infoFiles[e]?.size ?? 0;
      }
      onDirInfo(fileEntry, totalSize);
    }
    for (final dirEntry in directoryForFolders.entries) {
      onDirInfo(dirEntry, infoDirSize[dirEntry.key] ?? 0);
    }

    params.$2.send((infoFiles, infoDirs));
  }

  // Future<void> _fetchInfo(String forPath, List<File> files, List<Directory> dirs) async {
  //   final infoPort = await preparePortRaw(
  //     onResult: (result) {
  //       final res = result as (String, Map, Type);

  //       if (res.$1 != _currentFolderPath) return;
  //       setState(() {
  //         if (res.$2 is Map<String, NamidaFileStat>) {
  //           _currentInfoFiles = res.$2 as Map<String, NamidaFileStat>;
  //         } else if (res.$3 is Map<String, NamidaDirStat>) {
  //           _currentInfoDirs = res.$2 as Map<String, NamidaDirStat>;
  //         }
  //       });
  //     },
  //     isolateFunction: (itemsSendPort) async {
  //       await Isolate.spawn(_fetchInfoIsolate, itemsSendPort);
  //     },
  //   );
  //   final params = (forPath, files, dirs);
  //   infoPort.send(params);
  // }

  // static void _fetchInfoIsolate(SendPort sendPort) {
  //   final allFilesStats = <String, NamidaFileStat>{};
  //   final allDirsStats = <String, NamidaDirStat>{};

  //   final recievePort = ReceivePort();
  //   sendPort.send(recievePort.sendPort);

  //   StreamSubscription? streamSub;
  //   streamSub = recievePort.listen((p) async {
  //     if (PortsProvider.isDisposeMessage(p)) {
  //       recievePort.close();
  //       streamSub?.cancel();
  //       return;
  //     }
  //     p as (String forPath, List<File> files, List<Directory>);

  //     // -- files
  //     if (p.$2.isNotEmpty) {
  //       final newMapFiles = <String, NamidaFileStat>{};
  //       p.$2.forEach((e){
  //         try {
  //           if (allFilesStats[e.path] == null) {
  //             final stats = e.statSync();
  //             allFilesStats[e.path] = NamidaFileStat(
  //               size: stats.size,
  //               accessed: stats.accessed,
  //               changed: stats.changed,
  //               modified: stats.modified,
  //             );
  //           }
  //           newMapFiles[e.path] = allFilesStats[e.path]!;
  //         } catch (_) {}
  //       });
  //       sendPort.send((p.$1, newMapFiles, File));
  //     }

  //     // -- dirs
  //     if (p.$3.isNotEmpty) {
  //       final newMapDirs = <String, NamidaDirStat>{};
  //       p.$3.forEach((dir){
  //         try {
  //           int totalSize = 0;
  //           if (allDirsStats[dir.path] == null) {
  //             final dirStats = dir.statSync();
  //             var itemsInside = <FileSystemEntity>[];
  //             try {
  //               itemsInside = dir.listSync(recursive: true);
  //             } catch (e) {
  //               itemsInside = dir.listSync(recursive: false);
  //             }
  //             int filesCount = 0;
  //             int foldersCount = 0;
  //             itemsInside.forEach((file){
  //               if (file is File) {
  //                 // -- file stats inside each dir
  //                 final stats = file.statSync();
  //                 allFilesStats[file.path] ??= NamidaFileStat(
  //                   size: stats.size,
  //                   accessed: stats.accessed,
  //                   changed: stats.changed,
  //                   modified: stats.modified,
  //                 );
  //                 totalSize += stats.size;
  //                 filesCount++;
  //               } else {
  //                 foldersCount++;
  //               }
  //             });
  //             allDirsStats[dir.path] = NamidaDirStat(
  //               filesCount: filesCount,
  //               foldersCount: foldersCount,
  //               size: totalSize,
  //               accessed: dirStats.accessed,
  //               changed: dirStats.changed,
  //               modified: dirStats.modified,
  //             );
  //           }

  //           newMapDirs[dir.path] = allDirsStats[dir.path]!;
  //         } catch (_) {}
  //       });
  //       sendPort.send((p.$1, newMapDirs, Directory));
  //     }
  //   });
  // }

  static void _fetchFilesIsolate(({String dir, List<NamidaFileExtensionsWrapper> allowedExtensionsWrappers, bool showHiddenFiles, SendPort resultPort}) params) {
    List<FileSystemEntity> items;
    try {
      items = Directory(params.dir).listSync();
    } catch (e) {
      params.resultPort.send((<File>[], <Directory>[], e));
      return;
    }
    final files = <File>[];
    final dirs = <Directory>[];

    void onAdd(FileSystemEntity e) {
      if (e is File) {
        files.add(e);
      } else if (e is Directory) {
        dirs.add(e);
      }
    }

    final excludeHidden = params.showHiddenFiles == false;
    final extensionsWrappers = params.allowedExtensionsWrappers;

    if (excludeHidden && extensionsWrappers.isNotEmpty) {
      for (var e in items) {
        final filename = e.path.splitLast(_pathSeparator);
        if (e is Directory) {
          if (!filename.startsWith('.')) onAdd(e);
        } else {
          if (!filename.startsWith('.') && extensionsWrappers.any((wrapper) => wrapper.isPathValid(filename))) onAdd(e);
        }
      }
    } else if (excludeHidden) {
      for (var e in items) {
        final fileorDirName = e.path.splitLast(_pathSeparator);
        if (!fileorDirName.startsWith('.')) onAdd(e);
      }
    } else if (extensionsWrappers.isNotEmpty) {
      for (var e in items) {
        if (e is File) {
          final filename = e.path.splitLast(_pathSeparator);
          if (extensionsWrappers.any((wrapper) => wrapper.isPathValid(filename))) onAdd(e);
        } else {
          onAdd(e);
        }
      }
    } else {
      for (var e in items) {
        onAdd(e);
      }
    }

    files.sortBy((e) => _pathToName(e.path));
    dirs.sortBy((e) => _pathToName(e.path));
    params.resultPort.send((files, dirs, null));
  }

  final _effectiveAllowedExtensions = <NamidaFileExtensionsWrapper>[];

  @override
  void initState() {
    super.initState();
    _refreshPermissionStatus();
    NamidaStorage.inst.getStorageDirectories().then((paths) {
      _mainStoragePaths.addAll(paths);
      _fetchFiles(Directory(widget.initialDirectory ?? paths.first));
      _fetchInfo(_mainStoragePaths);
    });
    final allowedExtensions = widget.allowedExtensions;
    if (allowedExtensions != null) _effectiveAllowedExtensions.add(allowedExtensions);
    if (widget.memeType != NamidaStorageFileMemeType.any) {
      switch (widget.memeType) {
        case NamidaStorageFileMemeType.audio:
          _effectiveAllowedExtensions.add(NamidaFileExtensionsWrapper.audio);
        case NamidaStorageFileMemeType.video:
          _effectiveAllowedExtensions.add(NamidaFileExtensionsWrapper.video);
        case NamidaStorageFileMemeType.image:
          _effectiveAllowedExtensions.add(NamidaFileExtensionsWrapper.image);
        case NamidaStorageFileMemeType.media:
          _effectiveAllowedExtensions
            ..add(NamidaFileExtensionsWrapper.audio)
            ..add(NamidaFileExtensionsWrapper.video);
        case NamidaStorageFileMemeType.any:
      }
    }
    _initIconsLookup();

    if (isDesktop) {
      Timer(
        const Duration(milliseconds: 200), // give time for ui to layout to avoid hittest errors
        () => _onBackupPickerLaunch(_effectiveAllowedExtensions),
      );
    }
  }

  @override
  void dispose() {
    _stopMainIsolates();
    _stopInfoIsolates();
    _scrollController.dispose();
    _pathSplitsScrollController.dispose();
    _showHiddenFiles.close();
    _hasPermissionRx.close();
    super.dispose();
  }

  bool isPathRoot(String path) {
    return _mainStoragePaths.any(
      (element) {
        if (element == path) return true;
        if (!element.endsWith(_pathSeparator)) element += _pathSeparator;
        if (!path.endsWith(_pathSeparator)) path += _pathSeparator;
        return element == path;
      },
    );
  }

  final _scrollPositionsSaved = <String, double>{}; // path: offset

  void _navigateTo(Directory dir, {double? scrollOffset}) {
    try {
      _scrollPositionsSaved[_currentFolderPath] = _scrollController.offset; // saving current offset.
    } catch (_) {}
    _fetchFiles(dir);
    if (_scrollController.hasClients) _scrollController.jumpTo(scrollOffset ?? 0);
    try {
      WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
        if (_pathSplitsScrollController.hasClients) {
          _pathSplitsScrollController.animateTo(
            _pathSplitsScrollController.position.maxScrollExtent,
            duration: const Duration(milliseconds: 300),
            curve: Curves.easeOutQuart,
          );
        }
      });
    } catch (_) {}
  }

  void _navigateBack() {
    final pieces = _currentFolderPath.split(_pathSeparator);
    pieces.removeLast();
    final newPath = pieces.join(_pathSeparator);
    _navigateTo(Directory(newPath), scrollOffset: _scrollPositionsSaved[newPath]);
  }

  void _onSelectionComplete(List<T> items) {
    widget.onSelect.completeIfWasnt(items);
    widget.onPop();
  }

  final _selectedFiles = <File>[];
  final _selectedFilesLookup = <String, bool>{};
  void _onFileTap(File file) {
    if (_selectedFiles.isNotEmpty) {
      _onFileLongPress(file);
    } else {
      if (T == File) {
        _onSelectionComplete([file as T]);
      }
    }
  }

  void _onFileLongPress(File file) {
    if (T != File) return;

    final alreadySelected = _selectedFilesLookup[file.path] == true;

    if (_selectedFiles.isNotEmpty && !widget.allowMultiple) {
      _selectedFiles.clear();
      _selectedFilesLookup.clear();
    }

    if (alreadySelected) {
      setState(() {
        _selectedFiles.remove(file);
        _selectedFilesLookup[file.path] = false;
      });
    } else {
      setState(() {
        _selectedFiles.add(file);
        _selectedFilesLookup[file.path] = true;
      });
    }
  }

  final _selectedFolders = <Directory>[];
  final _selectedFoldersLookup = <String, bool>{};
  void _onFolderTap(Directory dir) {
    if (_selectedFolders.isNotEmpty) {
      _onFolderLongPress(dir);
    } else {
      _navigateTo(dir);
    }
  }

  void _onFolderLongPress(Directory dir) {
    if (T != Directory) return;

    final alreadySelected = _selectedFoldersLookup[dir.path] == true;

    if (_selectedFolders.isNotEmpty && !widget.allowMultiple) {
      _selectedFolders.clear();
      _selectedFoldersLookup.clear();
    }

    if (alreadySelected) {
      setState(() {
        _selectedFolders.remove(dir);
        _selectedFoldersLookup[dir.path] = false;
      });
    } else {
      setState(() {
        _selectedFolders.add(dir);
        _selectedFoldersLookup[dir.path] = true;
      });
    }
  }

  IconData _fileToIcon(File file) {
    final extension = _pathToExtension(file.path);
    final iconIndex = _iconsLookupPre[extension];
    if (iconIndex != null) {
      final icon = _iconsLookup[iconIndex];
      if (icon != null) return icon;
    }
    return Broken.document_1;
  }

  ArtworkWidget? _getFileImage(File file) {
    final extension = _pathToExtension(file.path);
    final iconIndex = _iconsLookupPre[extension];
    if (iconIndex != 2) return null;
    return ArtworkWidget(
      key: Key(file.path),
      thumbnailSize: 56.0,
      path: file.path,
      borderRadius: 8.0,
      blur: 4.0,
      disableBlurBgSizeShrink: true,
      fallbackToFolderCover: false,
      icon: _iconsLookup[2] ?? Broken.gallery,
    );
  }

  void _initIconsLookup() {
    for (final e in NamidaFileExtensionsWrapper.audio.extensions) {
      _iconsLookupPre[e] = 0;
    }
    for (final e in NamidaFileExtensionsWrapper.video.extensions) {
      _iconsLookupPre[e] = 1;
    }
    for (final e in NamidaFileExtensionsWrapper.image.extensions) {
      _iconsLookupPre[e] = 2;
    }
    for (final e in [...NamidaFileExtensionsWrapper.json.extensions, ...NamidaFileExtensionsWrapper.csv.extensions]) {
      _iconsLookupPre[e] = 3;
    }
    for (final e in NamidaFileExtensionsWrapper.m3u.extensions) {
      _iconsLookupPre[e] = 4;
    }
    for (final e in NamidaFileExtensionsWrapper.compressed.extensions) {
      _iconsLookupPre[e] = 5;
    }

    _iconsLookup[0] = Broken.musicnote;
    _iconsLookup[1] = Broken.video;
    _iconsLookup[2] = Broken.gallery;
    _iconsLookup[3] = Broken.document_code;
    _iconsLookup[4] = Broken.music_filter;
    _iconsLookup[5] = Broken.external_drive;
  }

  final _iconsLookupPre = <String, int>{};
  final _iconsLookup = <int, IconData>{};

  Future<void> _onBackupPickerLaunch([List<NamidaFileExtensionsWrapper>? allowedExtensions]) async {
    final note = widget.note != '' ? widget.note : null;
    if (T == File) {
      final res = await NamidaStorage.inst.pickFiles(
        note: note,
        multiple: widget.allowMultiple,
        memetype: widget.memeType,
        allowedExtensions: allowedExtensions,
        initialDirectory: widget.initialDirectory,
      );
      final files = res.map((e) => File(e)).toList();
      if (files.isNotEmpty) _onSelectionComplete(files as List<T>);
    } else if (T == Directory) {
      final res = await NamidaStorage.inst.pickDirectory(
        note: note,
        initialDirectory: widget.initialDirectory,
      );
      if (res != null) _onSelectionComplete([Directory(res) as T]);
    }
  }

  List<Widget> get _getCurrentPathsSplitsChildren {
    if (_currentFolderPath == '') return [];
    final currentRoot = _mainStoragePaths.firstWhere((element) => _currentFolderPath.startsWith(element));
    final pathWithoutRoot = _currentFolderPath.substring(currentRoot.length);
    final splits = pathWithoutRoot.split(_pathSeparator);
    final map = <int, String>{};

    if (splits.isNotEmpty) {
      final sdCardRegex = RegExp(r'\w{4}-\w{4}', caseSensitive: false);
      map[0] = sdCardRegex.hasMatch(currentRoot) ? 'SD Card' : 'Home';
      int index = 1;
      final splitsSkipped = Platform.isWindows ? splits.skip(0) : splits.skip(1);
      for (final part in splitsSkipped) {
        if (part.isNotEmpty) {
          map[index] = part;
        }
        index++;
      }
    }

    final textTheme = context.textTheme;
    final widgets = <Widget>[];
    for (final e in map.entries) {
      widgets.add(
        TapDetector(
          onTap: () {
            if (e.key == map.length - 1) return; // same path

            String newDirPath = currentRoot;
            if (newDirPath.endsWith(_pathSeparator)) newDirPath = currentRoot.substring(0, currentRoot.length - 1);
            for (final entry in map.entries.skip(1)) {
              if (entry.key > e.key) break;
              newDirPath += _pathSeparator + entry.value;
            }
            if (newDirPath.startsWith(currentRoot)) {
              _navigateTo(Directory(newDirPath));
            }
          },
          child: DecoratedBox(
            decoration: BoxDecoration(
              color: context.theme.cardColor,
              borderRadius: BorderRadius.circular(8.0.multipliedRadius),
            ),
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
              child: Text(
                e.value == '' ? _pathSeparator : e.value,
                style: textTheme.displayMedium,
              ),
            ),
          ),
        ),
      );
      widgets.add(
        const Icon(
          Broken.arrow_right_3,
          size: 16.0,
        ),
      );
    }
    widgets.removeLast();
    return widgets;
  }

  final _hasPermissionRx = true.obs; // assume yes until confirmed
  void _refreshPermissionStatus() async {
    _hasPermissionRx.value = await requestManageStoragePermission();
  }

  @override
  Widget build(BuildContext context) {
    final theme = context.theme;
    final textTheme = theme.textTheme;
    final chipColor = theme.cardColor;
    final pathSplitsChildren = _getCurrentPathsSplitsChildren;
    return NamidaPopScope(
      onWillPop: () async {
        if (isPathRoot(_currentFolderPath)) {
          _onSelectionComplete(<T>[]);
        } else {
          _navigateBack();
        }
        return false;
      },
      child: BackgroundWrapper(
        child: SafeArea(
          child: Column(
            children: [
              ObxO(
                rx: _hasPermissionRx,
                builder: (context, hasPermission) => hasPermission
                    ? const SizedBox()
                    : NamidaInkWell(
                        onTap: _refreshPermissionStatus,
                        borderRadius: 8.0,
                        bgColor: Colors.red.withOpacityExt(0.1),
                        margin: EdgeInsets.symmetric(horizontal: 8.0),
                        padding: EdgeInsets.symmetric(vertical: 10.0),
                        child: Row(
                          children: [
                            SizedBox(width: 8.0),
                            Icon(
                              Broken.warning_2,
                              size: 24.0,
                            ),
                            SizedBox(width: 8.0),
                            Expanded(
                              child: Text(
                                lang.grantStoragePermission,
                                style: textTheme.displayMedium?.copyWith(
                                  fontSize: 16.0,
                                ),
                              ),
                            ),
                            SizedBox(width: 8.0),
                            NamidaInkWellButton(
                              onTap: _refreshPermissionStatus,
                              borderRadius: 6.0,
                              icon: null,
                              text: lang.manage,
                            ),
                            SizedBox(width: 8.0),
                          ],
                        ),
                      ),
              ),
              Row(
                children: [
                  const SizedBox(width: 4.0),
                  IconButton(
                    onPressed: () {
                      _onSelectionComplete(<T>[]);
                    },
                    icon: const Icon(
                      Broken.arrow_left_2,
                      size: 24.0,
                    ),
                  ),
                  const SizedBox(width: 4.0),
                  Expanded(
                    child: widget.note != ''
                        ? Text(
                            widget.note.addDQuotation(),
                            style: textTheme.displayMedium?.copyWith(
                              fontSize: 16.0,
                            ),
                          )
                        : const SizedBox(),
                  ),
                  if (T == Directory)
                    IconButton(
                      tooltip: lang.folder,
                      padding: EdgeInsets.zero,
                      visualDensity: VisualDensity.compact,
                      style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
                      onPressed: () {
                        final dirController = TextEditingController();
                        NamidaNavigator.inst.navigateDialog(
                          onDisposing: () {
                            dirController.dispose();
                          },
                          dialogBuilder: (theme) => CustomBlurryDialog(
                            title: lang.folder,
                            actions: [
                              const CancelButton(),
                              NamidaButton(
                                text: lang.confirm,
                                onTap: () {
                                  final text = dirController.text;
                                  if (text.length > 2) {
                                    NamidaNavigator.inst.closeDialog();
                                    _onSelectionComplete([Directory(text) as T]);
                                  }
                                },
                              ),
                            ],
                            child: Column(
                              children: [
                                const SizedBox(height: 12.0),
                                CustomTagTextField(
                                  controller: dirController,
                                  hintText: '',
                                  labelText: lang.folder,
                                  validatorMode: AutovalidateMode.always,
                                  validator: (value) {
                                    value ??= '';
                                    if (value.isEmpty) {
                                      return lang.emptyValue;
                                    }
                                    try {
                                      if (!DirectoryIndexLocal(value).existsSync()) {
                                        return lang.directoryDoesntExist;
                                      }
                                    } catch (e) {
                                      return e.toString();
                                    }

                                    return null;
                                  },
                                ),
                                const SizedBox(height: 12.0),
                              ],
                            ),
                          ),
                        );
                      },
                      icon: Icon(
                        Broken.wallet_1,
                        size: 20.0,
                        color: context.defaultIconColor(),
                      ),
                    ),
                  LongPressDetector(
                    onLongPress: () => _onBackupPickerLaunch(), // launching without extensions filter
                    child: IconButton(
                      padding: EdgeInsets.zero,
                      visualDensity: VisualDensity.compact,
                      style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
                      onPressed: () => _onBackupPickerLaunch(_effectiveAllowedExtensions),
                      icon: Icon(
                        Broken.export_1,
                        size: 20.0,
                        color: context.defaultIconColor(),
                      ),
                    ),
                  ),
                  const SizedBox(width: 12.0),
                ],
              ),
              if (_mainStoragePaths.length > 1)
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
                  child: SizedBox(
                    width: context.width,
                    child: Wrap(
                      alignment: WrapAlignment.start,
                      crossAxisAlignment: WrapCrossAlignment.center,
                      runAlignment: WrapAlignment.start,
                      runSpacing: 6.0,
                      children: _mainStoragePaths
                          .map(
                            (e) => NamidaInkWell(
                              animationDurationMS: 200,
                              borderRadius: 8.0,
                              bgColor: _currentFolderPath.startsWith(e) ? theme.colorScheme.secondaryContainer : theme.cardColor,
                              onTap: () => _fetchFiles(Directory(e)),
                              margin: const EdgeInsets.symmetric(horizontal: 4.0),
                              padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
                              child: Text(
                                e,
                                style: textTheme.displaySmall?.copyWith(fontWeight: FontWeight.w600),
                              ),
                            ),
                          )
                          .toFixedList(),
                    ),
                  ),
                ),
              if (pathSplitsChildren.isNotEmpty)
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
                  child: SizedBox(
                    width: context.width,
                    child: SmoothSingleChildScrollView(
                      controller: _pathSplitsScrollController,
                      scrollDirection: Axis.horizontal,
                      child: Row(
                        children: pathSplitsChildren,
                      ),
                    ),
                  ),
                ),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
                child: Row(
                  mainAxisAlignment: .end,
                  children: [
                    if (_currentFolders.isNotEmpty || _currentFiles.isNotEmpty)
                      Expanded(
                        child: Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 4.0),
                          child: Text(
                            [
                              if (_currentFolders.isNotEmpty) _currentFolders.length.displayFolderKeyword,
                              if (_currentFiles.isNotEmpty) _currentFiles.length.displayFilesKeyword,
                            ].join(' | '),
                            style: textTheme.displayMedium,
                          ),
                        ),
                      ),
                    if (T == Directory)
                      IconButton(
                        tooltip: lang.newDirectory,
                        padding: EdgeInsets.zero,
                        visualDensity: VisualDensity.compact,
                        style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
                        onPressed: () {
                          final dirController = TextEditingController();
                          NamidaNavigator.inst.navigateDialog(
                            onDisposing: () {
                              dirController.dispose();
                            },
                            dialogBuilder: (theme) => CustomBlurryDialog(
                              title: lang.newDirectory,
                              actions: [
                                const CancelButton(),
                                NamidaButton(
                                  text: lang.create,
                                  onTap: () async {
                                    final name = dirController.text;
                                    final fullPath = FileParts.joinPath(_currentFolderPath, name);
                                    try {
                                      Directory(fullPath).createSync(recursive: true);
                                      await _fetchFiles(Directory(_currentFolderPath), clearPrevious: false);
                                    } catch (e) {
                                      snackyy(title: lang.error, message: e.toString(), isError: true);
                                    }
                                    NamidaNavigator.inst.closeDialog();
                                  },
                                ),
                              ],
                              child: Column(
                                children: [
                                  const SizedBox(height: 12.0),
                                  CustomTagTextField(
                                    controller: dirController,
                                    hintText: '',
                                    labelText: lang.newDirectory,
                                    validatorMode: AutovalidateMode.always,
                                    validator: (name) {
                                      name ??= '';
                                      if (name.isEmpty) {
                                        return lang.pleaseEnterAName;
                                      }
                                      try {
                                        final fullPath = FileParts.joinPath(_currentFolderPath, name);
                                        if (DirectoryIndexLocal(fullPath).existsSync()) {
                                          return lang.alreadyExists;
                                        }
                                      } catch (e) {
                                        return e.toString();
                                      }

                                      return null;
                                    },
                                  ),
                                  const SizedBox(height: 12.0),
                                ],
                              ),
                            ),
                          );
                        },
                        icon: Icon(
                          Broken.add_circle,
                          size: 20.0,
                          color: context.defaultIconColor(),
                        ),
                      ),
                    IconButton(
                      tooltip: 'Show empty folders',
                      padding: EdgeInsets.zero,
                      visualDensity: VisualDensity.compact,
                      style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
                      onPressed: () {
                        setState(() => _showEmptyFolders = !_showEmptyFolders);
                      },
                      icon: StackedIcon(
                        baseIcon: Broken.folder,
                        secondaryIcon: _showEmptyFolders ? Broken.eye : Broken.eye_slash,
                        iconSize: 20.0,
                        secondaryIconSize: 12.0,
                        disableColor: _showEmptyFolders,
                      ),
                    ),
                    IconButton(
                      tooltip: 'Show hidden files/folders',
                      padding: EdgeInsets.zero,
                      visualDensity: VisualDensity.compact,
                      style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
                      onPressed: () {
                        _showHiddenFiles.value = !_showHiddenFiles.value;
                        _fetchFiles(Directory(_currentFolderPath));
                      },
                      icon: Obx(
                        (context) => Icon(
                          _showHiddenFiles.valueR ? Broken.eye : Broken.eye_slash,
                          size: 20.0,
                          color: _showHiddenFiles.valueR ? null : context.defaultIconColor(),
                        ),
                      ),
                    ),
                    Obx(
                      (context) => SortByMenu(
                        title: _sortTypeToName[settings.fileBrowserSort.valueR] ?? '',
                        popupMenuChild: SortByMenuCustom(
                          childrenCallback: (context) {
                            Widget getTile(IconData icon, String title, FileBrowserSortType sort) {
                              return ObxO(
                                rx: settings.fileBrowserSort,
                                builder: (context, currentSort) => SmallListTile(
                                  borderRadius: 12.0,
                                  visualDensity: const VisualDensity(horizontal: -4, vertical: -3.5),
                                  trailing: Padding(
                                    padding: const EdgeInsets.only(right: 4.0),
                                    child: Icon(icon, size: 20.0),
                                  ),
                                  title: title,
                                  active: currentSort == sort,
                                  onTap: () => _sortItems(sort, null),
                                ),
                              );
                            }

                            return [
                              Padding(
                                padding: const EdgeInsets.only(left: 4.0, right: 4.0, bottom: 4.0),
                                child: ListTileWithCheckMark(
                                  borderRadius: 10.0,
                                  activeRx: settings.fileBrowserSortReversed,
                                  onTap: () => _sortItems(null, !settings.fileBrowserSortReversed.value),
                                ),
                              ),
                              getTile(Broken.text, lang.fileName, FileBrowserSortType.name),
                              getTile(Broken.calendar, lang.date, FileBrowserSortType.dateModified),
                              getTile(Broken.document_code, lang.extension, FileBrowserSortType.type),
                              getTile(Broken.math, lang.size, FileBrowserSortType.size),
                            ];
                          },
                        ),
                        isCurrentlyReversed: settings.fileBrowserSortReversed.valueR,
                        onReverseIconTap: () => _sortItems(null, !settings.fileBrowserSortReversed.value),
                      ),
                    ),
                    const SizedBox(width: 8.0),
                  ],
                ),
              ),
              Expanded(
                child: Stack(
                  children: [
                    Listener(
                      onPointerMove: (event) {
                        onPointerMove(_scrollController, event);
                      },
                      onPointerUp: (event) async {
                        onRefresh(() async => await _fetchFiles(Directory(_currentFolderPath), clearPrevious: false));
                      },
                      onPointerCancel: (event) => onVerticalDragFinish(),
                      child: _isFetching
                          ? Center(
                              key: const Key('loading'),
                              child: ThreeArchedCircle(
                                color: theme.colorScheme.primary.withOpacityExt(0.5),
                                size: 56.0,
                              ),
                            )
                          : _currentFolders.isEmpty && _currentFiles.isEmpty
                          ? SizedBox(
                              width: context.width,
                              child: Column(
                                key: const Key('empty'),
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  const Icon(
                                    Broken.emoji_sad,
                                    size: 42.0,
                                  ),
                                  const SizedBox(height: 12.0),
                                  Text(
                                    "0 ${lang.files}",
                                    style: textTheme.displayLarge,
                                  ),
                                ],
                              ),
                            )
                          : AnimationLimiter(
                              key: const Key('items'),
                              child: NamidaScrollbar(
                                controller: _scrollController,
                                child: SmoothCustomScrollView(
                                  controller: _scrollController,
                                  slivers: [
                                    SuperSliverList.builder(
                                      itemCount: _currentFolders.length,
                                      itemBuilder: (context, index) {
                                        final folder = _currentFolders[index];
                                        final info = _currentInfoDirs[folder.path];
                                        if (info == null && !_fetchingInfo && !_showEmptyFolders) return const SizedBox();
                                        return _FileSystemChip(
                                          position: index,
                                          bgColor: chipColor,
                                          onTap: () => _onFolderTap(folder),
                                          onLongPress: () => _onFolderLongPress(folder),
                                          displayCheckMark: _selectedFolders.isNotEmpty,
                                          selected: _selectedFoldersLookup[folder.path] == true,
                                          icon: Broken.folder,
                                          title: _pathToName(folder.path),
                                          subtitle: info == null
                                              ? 0.fileSizeFormatted
                                              : [
                                                  "${info.size.fileSizeFormatted}${info.accurate ? '' : '?'}",
                                                  if (info.filesCount > 0) "${info.filesCount.displayFilesKeyword}${info.accurate ? '' : '?'}",
                                                  if (info.foldersCount > 0) "${info.foldersCount.displayFolderKeyword}${info.accurate ? '' : '?'}",
                                                ].join(' | '),
                                        );
                                      },
                                    ),
                                    if (_currentFolders.isNotEmpty && _currentFiles.isNotEmpty)
                                      const SliverToBoxAdapter(
                                        child: NamidaContainerDivider(
                                          margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
                                        ),
                                      ),
                                    SuperSliverList.builder(
                                      itemCount: _currentFiles.length,
                                      itemBuilder: (context, index) {
                                        final file = _currentFiles[index];
                                        final info = _currentInfoFiles[file.path];
                                        final image = _getFileImage(file);
                                        return _FileSystemChip(
                                          position: index + _currentFolders.length + 1,
                                          bgColor: chipColor,
                                          onTap: () => _onFileTap(file),
                                          onLongPress: () => _onFileLongPress(file),
                                          displayCheckMark: _selectedFiles.isNotEmpty,
                                          selected: _selectedFilesLookup[file.path] == true,
                                          icon: image == null ? _fileToIcon(file) : null,
                                          leading: image != null ? _getFileImage(file) : null,
                                          title: _pathToName(file.path),
                                          subtitle: info == null ? '' : "${info.size.fileSizeFormatted} | ${info.modified.dateAndClockFormattedOriginal}",
                                        );
                                      },
                                    ),
                                  ],
                                ),
                              ),
                            ),
                    ),
                    pullToRefreshWidget,
                    Positioned(
                      bottom: 12.0,
                      right: 12.0,
                      child: CustomAnimatedSwitcher(
                        duration: const Duration(milliseconds: 200),
                        child: T == File && _selectedFiles.isNotEmpty
                            ? NamidaFABButton(
                                big: true,
                                icon: Broken.tick_square,
                                text: _selectedFiles.length.displayFilesKeyword,
                                onTap: () => _onSelectionComplete(_selectedFiles as List<T>),
                              )
                            : T == Directory && (_selectedFolders.isNotEmpty || !isPathRoot(_currentFolderPath))
                            ? NamidaFABButton(
                                big: true,
                                icon: Broken.tick_square,
                                onTap: () => _onSelectionComplete(
                                  _selectedFolders.isNotEmpty ? _selectedFolders as List<T> : [Directory(_currentFolderPath) as T],
                                ),
                              )
                            : const SizedBox(),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _FileSystemChip extends StatelessWidget {
  final String title;
  final String subtitle;
  final IconData? icon;
  final Color bgColor;
  final VoidCallback onTap;
  final VoidCallback onLongPress;
  final int position;
  final bool displayCheckMark;
  final bool selected;
  final Widget? leading;

  const _FileSystemChip({
    required this.title,
    required this.subtitle,
    required this.icon,
    required this.bgColor,
    required this.onTap,
    required this.onLongPress,
    required this.position,
    required this.displayCheckMark,
    required this.selected,
    this.leading,
  });

  @override
  Widget build(BuildContext context) {
    final textTheme = context.textTheme;
    return NamidaInkWell(
      borderRadius: 8.0,
      bgColor: bgColor,
      margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0),
      padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
      onTap: onTap,
      onLongPress: onLongPress,
      child: Row(
        children: [
          leading ?? Icon(icon),
          const SizedBox(width: 8.0),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(
                  title,
                  style: textTheme.displaySmall?.copyWith(
                    fontSize: 13.0,
                    fontWeight: FontWeight.w600,
                  ),
                ),
                const SizedBox(height: 2.0),
                Text(
                  subtitle,
                  style: textTheme.displaySmall?.copyWith(
                    fontSize: 12.0,
                    fontWeight: FontWeight.w400,
                    // color: theme.colorScheme.onSurface.withOpacityExt(0.7),
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(width: 4.0),
          NamidaCheckMark(
            size: 16.0,
            active: selected,
          ).animateEntrance(
            showWhen: displayCheckMark,
            durationMS: 200,
          ),
        ],
      ),
    );
  }
}

class NamidaFileStat {
  final int size;
  final DateTime accessed;
  final DateTime changed;
  final DateTime modified;

  const NamidaFileStat({
    required this.size,
    required this.accessed,
    required this.changed,
    required this.modified,
  });
}

class NamidaDirStat {
  final bool accurate;
  final int filesCount;
  final int foldersCount;
  final int size;
  final DateTime accessed;
  final DateTime changed;
  final DateTime modified;

  const NamidaDirStat({
    required this.accurate,
    required this.filesCount,
    required this.foldersCount,
    required this.size,
    required this.accessed,
    required this.changed,
    required this.modified,
  });
}
