part of '../neon_files.dart';

class FilesBrowserView extends StatefulWidget {
  const FilesBrowserView({
    required this.bloc,
    required this.filesBloc,
    this.onPickFile,
    this.enableFileActions = true,
    this.enableCreateActions = true,
    this.onlyShowDirectories = false,
    super.key,
    // ignore: prefer_asserts_with_message
  }) : assert((onPickFile == null) == onlyShowDirectories);

  final FilesBrowserBloc bloc;
  final FilesBloc filesBloc;
  final Function(FileDetails)? onPickFile;
  final bool enableFileActions;
  final bool enableCreateActions;
  final bool onlyShowDirectories;

  @override
  State<FilesBrowserView> createState() => _FilesBrowserViewState();
}

class _FilesBrowserViewState extends State<FilesBrowserView> {
  @override
  void initState() {
    super.initState();

    widget.bloc.errors.listen((final error) {
      NeonException.showSnackbar(context, error);
    });
  }

  @override
  Widget build(final BuildContext context) => ResultBuilder<FilesBrowserBloc, List<WebDavFile>>(
        stream: widget.bloc.files,
        builder: (final context, final files) => StreamBuilder<List<String>>(
          stream: widget.bloc.path,
          builder: (final context, final pathSnapshot) => StreamBuilder<List<UploadTask>>(
            stream: widget.filesBloc.uploadTasks,
            builder: (final context, final uploadTasksSnapshot) => StreamBuilder<List<DownloadTask>>(
              stream: widget.filesBloc.downloadTasks,
              builder: (final context, final downloadTasksSnapshot) => !pathSnapshot.hasData ||
                      !uploadTasksSnapshot.hasData ||
                      !downloadTasksSnapshot.hasData
                  ? Container()
                  : BackButtonListener(
                      onBackButtonPressed: () async {
                        final path = pathSnapshot.data!;
                        if (path.isNotEmpty) {
                          widget.bloc.setPath(path.sublist(0, path.length - 1));
                          return true;
                        }
                        return false;
                      },
                      child: Scaffold(
                        resizeToAvoidBottomInset: false,
                        floatingActionButton: widget.enableCreateActions
                            ? FloatingActionButton(
                                onPressed: () async {
                                  await showDialog(
                                    context: context,
                                    builder: (final context) => FilesChooseCreateDialog(
                                      bloc: widget.filesBloc,
                                      basePath: widget.bloc.path.value,
                                    ),
                                  );
                                },
                                child: const Icon(Icons.add),
                              )
                            : null,
                        body: SortBoxBuilder<FilesSortProperty, WebDavFile>(
                          sortBox: filesSortBox,
                          sortPropertyOption: widget.bloc.options.filesSortPropertyOption,
                          sortBoxOrderOption: widget.bloc.options.filesSortBoxOrderOption,
                          input: files.data,
                          builder: (final context, final sorted) => NeonListView<Widget>(
                            scrollKey: 'files-${pathSnapshot.data!.join('/')}',
                            withFloatingActionButton: true,
                            items: [
                              for (final uploadTask in sorted == null
                                  ? <UploadTask>[]
                                  : uploadTasksSnapshot.data!.where(
                                      (final task) =>
                                          sorted.where((final file) => _pathMatchesFile(task.path, file.name)).isEmpty,
                                    )) ...[
                                StreamBuilder<int>(
                                  stream: uploadTask.progress,
                                  builder: (final context, final uploadTaskProgressSnapshot) =>
                                      !uploadTaskProgressSnapshot.hasData
                                          ? Container()
                                          : _buildFile(
                                              context: context,
                                              details: FileDetails(
                                                path: uploadTask.path,
                                                isDirectory: false,
                                                size: uploadTask.size,
                                                etag: null,
                                                mimeType: null,
                                                lastModified: uploadTask.lastModified,
                                                hasPreview: null,
                                                isFavorite: null,
                                              ),
                                              uploadProgress: uploadTaskProgressSnapshot.data,
                                              downloadProgress: null,
                                            ),
                                ),
                              ],
                              if (sorted != null) ...[
                                for (final file in sorted) ...[
                                  if (!widget.onlyShowDirectories || file.isDirectory) ...[
                                    Builder(
                                      builder: (final context) {
                                        final matchingUploadTasks = uploadTasksSnapshot.data!
                                            .where((final task) => _pathMatchesFile(task.path, file.name));
                                        final matchingDownloadTasks = downloadTasksSnapshot.data!
                                            .where((final task) => _pathMatchesFile(task.path, file.name));

                                        return StreamBuilder<int?>(
                                          stream: matchingUploadTasks.isNotEmpty
                                              ? matchingUploadTasks.first.progress
                                              : Stream.value(null),
                                          builder: (final context, final uploadTaskProgressSnapshot) =>
                                              StreamBuilder<int?>(
                                            stream: matchingDownloadTasks.isNotEmpty
                                                ? matchingDownloadTasks.first.progress
                                                : Stream.value(null),
                                            builder: (final context, final downloadTaskProgressSnapshot) => _buildFile(
                                              context: context,
                                              details: FileDetails(
                                                path: [...widget.bloc.path.value, file.name],
                                                isDirectory: matchingUploadTasks.isEmpty && file.isDirectory,
                                                size: matchingUploadTasks.isNotEmpty
                                                    ? matchingUploadTasks.first.size
                                                    : file.size!,
                                                etag: matchingUploadTasks.isNotEmpty ? null : file.etag,
                                                mimeType: matchingUploadTasks.isNotEmpty ? null : file.mimeType,
                                                lastModified: matchingUploadTasks.isNotEmpty
                                                    ? matchingUploadTasks.first.lastModified
                                                    : file.lastModified!,
                                                hasPreview: matchingUploadTasks.isNotEmpty ? null : file.hasPreview,
                                                isFavorite: matchingUploadTasks.isNotEmpty ? null : file.favorite,
                                              ),
                                              uploadProgress: uploadTaskProgressSnapshot.data,
                                              downloadProgress: downloadTaskProgressSnapshot.data,
                                            ),
                                          ),
                                        );
                                      },
                                    ),
                                  ],
                                ],
                              ],
                            ],
                            isLoading: files.loading,
                            error: files.error,
                            onRefresh: widget.bloc.refresh,
                            builder: (final context, final widget) => widget,
                            topScrollingChildren: [
                              Align(
                                alignment: Alignment.topLeft,
                                child: Container(
                                  margin: const EdgeInsets.symmetric(
                                    horizontal: 10,
                                  ),
                                  child: Wrap(
                                    crossAxisAlignment: WrapCrossAlignment.center,
                                    children: <Widget>[
                                      SizedBox(
                                        height: 40,
                                        child: InkWell(
                                          onTap: () {
                                            widget.bloc.setPath([]);
                                          },
                                          child: const Icon(Icons.house),
                                        ),
                                      ),
                                      for (var i = 0; i < pathSnapshot.data!.length; i++) ...[
                                        InkWell(
                                          onTap: () {
                                            widget.bloc.setPath(pathSnapshot.data!.sublist(0, i + 1));
                                          },
                                          child: Text(pathSnapshot.data![i]),
                                        ),
                                      ],
                                    ]
                                        .intersperse(
                                          const Icon(
                                            Icons.keyboard_arrow_right,
                                            size: 40,
                                          ),
                                        )
                                        .toList(),
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
            ),
          ),
        ),
      );

  bool _pathMatchesFile(final List<String> path, final String name) => const ListEquality().equals(
        [...widget.bloc.path.value, name],
        path,
      );

  Widget _buildFile({
    required final BuildContext context,
    required final FileDetails details,
    required final int? uploadProgress,
    required final int? downloadProgress,
  }) =>
      ListTile(
        // When the ETag is null it means we are uploading this file right now
        onTap: details.isDirectory || details.etag != null
            ? () async {
                if (details.isDirectory) {
                  widget.bloc.setPath(details.path);
                } else {
                  if (widget.onPickFile != null) {
                    widget.onPickFile!.call(details);
                  }
                }
              }
            : null,
        title: Text(
          details.name,
          overflow: TextOverflow.ellipsis,
        ),
        subtitle: Row(
          children: [
            RelativeTime(
              date: details.lastModified,
            ),
            if (details.size > 0) ...[
              const SizedBox(
                width: 10,
              ),
              Text(
                filesize(details.size, 1),
                style: DefaultTextStyle.of(context).style.copyWith(
                      color: Colors.grey,
                    ),
              ),
            ],
          ],
        ),
        leading: SizedBox.square(
          dimension: 40,
          child: Stack(
            children: [
              Center(
                child: uploadProgress != null || downloadProgress != null
                    ? Column(
                        children: [
                          Icon(
                            uploadProgress != null ? MdiIcons.upload : MdiIcons.download,
                            color: Theme.of(context).colorScheme.primary,
                          ),
                          LinearProgressIndicator(
                            value: (uploadProgress ?? downloadProgress)! / 100,
                          ),
                        ],
                      )
                    : FilePreview(
                        bloc: widget.filesBloc,
                        details: details,
                        withBackground: true,
                        borderRadius: const BorderRadius.all(Radius.circular(8)),
                      ),
              ),
              if (details.isFavorite ?? false) ...[
                const Align(
                  alignment: Alignment.bottomRight,
                  child: Icon(
                    Icons.star,
                    size: 14,
                    color: Colors.yellow,
                  ),
                ),
              ],
            ],
          ),
        ),
        trailing: uploadProgress == null && downloadProgress == null && widget.enableFileActions
            ? PopupMenuButton<FilesFileAction>(
                itemBuilder: (final context) => [
                  if (details.isFavorite != null) ...[
                    PopupMenuItem(
                      value: FilesFileAction.toggleFavorite,
                      child: Text(
                        details.isFavorite!
                            ? AppLocalizations.of(context).filesRemoveFromFavorites
                            : AppLocalizations.of(context).filesAddToFavorites,
                      ),
                    ),
                  ],
                  PopupMenuItem(
                    value: FilesFileAction.details,
                    child: Text(AppLocalizations.of(context).filesDetails),
                  ),
                  PopupMenuItem(
                    value: FilesFileAction.rename,
                    child: Text(AppLocalizations.of(context).rename),
                  ),
                  PopupMenuItem(
                    value: FilesFileAction.move,
                    child: Text(AppLocalizations.of(context).move),
                  ),
                  PopupMenuItem(
                    value: FilesFileAction.copy,
                    child: Text(AppLocalizations.of(context).copy),
                  ),
                  // TODO: https://github.com/provokateurin/nextcloud-neon/issues/4
                  if (!details.isDirectory) ...[
                    PopupMenuItem(
                      value: FilesFileAction.sync,
                      child: Text(AppLocalizations.of(context).filesSync),
                    ),
                  ],
                  PopupMenuItem(
                    value: FilesFileAction.delete,
                    child: Text(AppLocalizations.of(context).delete),
                  ),
                ],
                onSelected: (final action) async {
                  switch (action) {
                    case FilesFileAction.toggleFavorite:
                      if (details.isFavorite ?? false) {
                        widget.filesBloc.removeFavorite(details.path);
                      } else {
                        widget.filesBloc.addFavorite(details.path);
                      }
                      break;
                    case FilesFileAction.details:
                      await Navigator.of(context).push(
                        MaterialPageRoute(
                          builder: (final context) => FilesDetailsPage(
                            bloc: widget.filesBloc,
                            details: details,
                          ),
                        ),
                      );
                      break;
                    case FilesFileAction.rename:
                      final result = await showRenameDialog(
                        context: context,
                        title: details.isDirectory
                            ? AppLocalizations.of(context).filesRenameFolder
                            : AppLocalizations.of(context).filesRenameFile,
                        value: details.name,
                      );
                      if (result != null) {
                        widget.filesBloc.rename(details.path, result);
                      }
                      break;
                    case FilesFileAction.move:
                      final b = widget.filesBloc.getNewFilesBrowserBloc();
                      final originalPath = details.path.sublist(0, details.path.length - 1);
                      b.setPath(originalPath);
                      final result = await showDialog<List<String>?>(
                        context: context,
                        builder: (final context) => FilesChooseFolderDialog(
                          bloc: b,
                          filesBloc: widget.filesBloc,
                          originalPath: originalPath,
                        ),
                      );
                      b.dispose();
                      if (result != null) {
                        widget.filesBloc.move(details.path, result..add(details.name));
                      }
                      break;
                    case FilesFileAction.copy:
                      final b = widget.filesBloc.getNewFilesBrowserBloc();
                      final originalPath = details.path.sublist(0, details.path.length - 1);
                      b.setPath(originalPath);
                      final result = await showDialog<List<String>?>(
                        context: context,
                        builder: (final context) => FilesChooseFolderDialog(
                          bloc: b,
                          filesBloc: widget.filesBloc,
                          originalPath: originalPath,
                        ),
                      );
                      b.dispose();
                      if (result != null) {
                        widget.filesBloc.copy(details.path, result..add(details.name));
                      }
                      break;
                    case FilesFileAction.sync:
                      final sizeWarning = widget.bloc.options.downloadSizeWarning.value;
                      if (sizeWarning != null && details.size > sizeWarning) {
                        // ignore: use_build_context_synchronously
                        if (!(await showConfirmationDialog(
                          context,
                          // ignore: use_build_context_synchronously
                          AppLocalizations.of(context).filesConfirmDownloadSizeWarning(
                            filesize(sizeWarning),
                            filesize(details.size),
                          ),
                        ))) {
                          return;
                        }
                      }
                      widget.filesBloc.syncFile(details.path);
                      break;
                    case FilesFileAction.delete:
                      // ignore: use_build_context_synchronously
                      if (await showConfirmationDialog(
                        context,
                        details.isDirectory
                            // ignore: use_build_context_synchronously
                            ? AppLocalizations.of(context).filesDeleteFolderConfirm(details.name)
                            // ignore: use_build_context_synchronously
                            : AppLocalizations.of(context).filesDeleteFileConfirm(details.name),
                      )) {
                        widget.filesBloc.delete(details.path);
                      }
                      break;
                  }
                },
              )
            : const SizedBox.square(
                dimension: 48,
              ),
      );
}

enum FilesFileAction {
  toggleFavorite,
  details,
  rename,
  move,
  copy,
  sync,
  delete,
}