import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';

import '../../../api/errors/error_mapper.dart';
import '../../../routing/app_routes.dart';
import '../../../share_intent/internal_share_actions.dart';
import '../../../share_intent/pending_share.dart';
import '../../../share_intent/remote_file_ref.dart';
import '../../../share_intent/share_intent_listener.dart';
import '../../../state/app/infrastructure/loadable_state/loadable_state.dart';
import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart';
import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart';
import '../../../state/app/modules/files/bloc/files_bloc.dart';
import '../../../state/app/modules/files/bloc/files_state.dart';
import '../../../state/app/modules/settings/bloc/settings_cubit.dart';
import '../../../widget/centered_leading.dart';
import '../../../widget/info_dialog.dart';
import '../../../widget/placeholder_view.dart';
import '../files/data/sort_options.dart';
import '../files/files_upload_dialog.dart';
import '../files/widgets/add_file_menu.dart';
import '../files/widgets/file_leading.dart';
import '../files/widgets/files_sort_actions.dart';

typedef _FolderConfirmedCallback =
    Future<void> Function(BuildContext context, List<String> targetPath);

class ShareFolderPicker extends StatelessWidget {
  final String _fabLabel;
  final _FolderConfirmedCallback _onConfirm;

  const ShareFolderPicker._({
    required String fabLabel,
    required _FolderConfirmedCallback onConfirm,
  }) : _fabLabel = fabLabel,
       _onConfirm = onConfirm;

  /// External share-intent flow: upload local files into the chosen folder.
  factory ShareFolderPicker.forExternalShare({required PendingShare share}) =>
      ShareFolderPicker._(
        fabLabel: 'Hier hochladen',
        onConfirm: (ctx, target) => _externalUploadFlow(ctx, target, share),
      );

  /// In-app save flow: server-to-server WebDAV-copy of an existing file into
  /// the chosen folder.
  factory ShareFolderPicker.forInternalSave({required RemoteFileRef file}) =>
      ShareFolderPicker._(
        fabLabel: 'Hierhin kopieren',
        onConfirm: (ctx, target) => _internalCopyFlow(ctx, target, file),
      );

  @override
  Widget build(BuildContext context) =>
      BlocModule<FilesBloc, LoadableState<FilesState>>(
        create: (_) => FilesBloc(),
        child: (context, _, _) =>
            _ShareFolderPickerView(fabLabel: _fabLabel, onConfirm: _onConfirm),
      );
}

class _ShareFolderPickerView extends StatefulWidget {
  final String fabLabel;
  final _FolderConfirmedCallback onConfirm;
  const _ShareFolderPickerView({
    required this.fabLabel,
    required this.onConfirm,
  });

  @override
  State<_ShareFolderPickerView> createState() => _ShareFolderPickerViewState();
}

class _ShareFolderPickerViewState extends State<_ShareFolderPickerView> {
  late final SettingsCubit _settings;
  late SortOption _currentSort;
  late bool _ascending;

  @override
  void initState() {
    super.initState();
    _settings = context.read<SettingsCubit>();
    _currentSort = _settings.val().fileSettings.sortBy;
    _ascending = _settings.val().fileSettings.ascending;
  }

  void _enter(FilesBloc bloc, List<String> currentPath, String folderName) {
    bloc.setPath([...currentPath, folderName]);
  }

  void _goUp(FilesBloc bloc, List<String> currentPath) {
    if (currentPath.isEmpty) return;
    bloc.setPath(currentPath.sublist(0, currentPath.length - 1));
  }

  @override
  Widget build(BuildContext context) {
    final bloc = context.read<FilesBloc>();
    return BlocBuilder<FilesBloc, LoadableState<FilesState>>(
      buildWhen: (a, b) => a.data?.currentPath != b.data?.currentPath,
      builder: (_, outerState) {
        final currentPath = outerState.data?.currentPath ?? const [];
        return PopScope(
          // Back navigates one level up while inside a sub-folder; only the
          // root level actually closes the picker. Matches the standard
          // files-app pattern and keeps the AppBar back-arrow consistent
          // with the chat picker.
          canPop: currentPath.isEmpty,
          onPopInvokedWithResult: (didPop, _) {
            if (didPop) return;
            if (currentPath.isNotEmpty) _goUp(bloc, currentPath);
          },
          child: _buildScaffold(context, bloc, currentPath),
        );
      },
    );
  }

  Widget _buildScaffold(
    BuildContext context,
    FilesBloc bloc,
    List<String> currentPath,
  ) => Scaffold(
    appBar: AppBar(
      title: Text(
        currentPath.isEmpty ? 'Ordner wählen' : '/${currentPath.join('/')}',
        overflow: TextOverflow.ellipsis,
      ),
      actions: [
        IconButton(
          icon: const Icon(Icons.create_new_folder_outlined),
          tooltip: 'Ordner erstellen',
          onPressed: () => showCreateFolderDialog(context, bloc),
        ),
        FilesSortActions(
          currentSort: _currentSort,
          ascending: _ascending,
          onDirectionChanged: (e) {
            setState(() {
              _ascending = e;
              _settings.val(write: true).fileSettings.ascending = e;
            });
          },
          onSortChanged: (e) {
            setState(() {
              _currentSort = e;
              _settings.val(write: true).fileSettings.sortBy = e;
            });
          },
        ),
      ],
    ),
    floatingActionButton: FloatingActionButton.extended(
      heroTag: 'shareUploadHere',
      onPressed: () => widget.onConfirm(context, currentPath),
      icon: const Icon(Icons.upload),
      label: Text(widget.fabLabel),
    ),
    body: LoadableStateConsumer<FilesBloc, FilesState>(
      isReady: (state) => state.listing != null,
      child: (state, _) {
        final listing = state.listing!;
        final entries = listing.sortBy(
          sortOption: _currentSort,
          foldersToTop: _settings.val().fileSettings.sortFoldersToTop,
          reversed: _ascending,
        );

        if (entries.isEmpty) {
          return PlaceholderView(
            icon: Icons.folder_off_rounded,
            text: state.currentPath.isEmpty
                ? 'Leer. Du kannst hier direkt hochladen.'
                : 'Ordner ist leer. Du kannst hier hochladen.',
          );
        }

        return ListView.builder(
          padding: EdgeInsets.zero,
          itemCount: entries.length,
          itemBuilder: (context, i) {
            final entry = entries[i];
            if (entry.isDirectory) {
              return ListTile(
                leading: CenteredLeading(FileLeading(file: entry)),
                title: Text(entry.name),
                trailing: const Icon(Icons.chevron_right),
                onTap: () => _enter(bloc, state.currentPath, entry.name),
              );
            }
            return ListTile(
              enabled: false,
              leading: CenteredLeading(FileLeading(file: entry)),
              title: Text(entry.name),
            );
          },
        );
      },
    ),
  );
}

Future<void> _externalUploadFlow(
  BuildContext context,
  List<String> targetPath,
  PendingShare share,
) async {
  await pushScreen(
    context,
    withNavBar: false,
    screen: FilesUploadDialog(
      filePaths: share.filePaths,
      remotePath: targetPath.join('/'),
      onUploadFinished: (_) =>
          _afterExternalUploaded(context, targetPath, share),
    ),
  );
}

void _afterExternalUploaded(
  BuildContext context,
  List<String> targetPath,
  PendingShare share,
) {
  ShareIntentListener.instance.clear(ifCurrent: share);
  if (!context.mounted) return;
  _finishWithFolder(context, targetPath);
}

/// Closes any picker pages stacked on top of the current tab and jumps to
/// the chosen folder. Shared by external upload + internal copy flows.
void _finishWithFolder(BuildContext context, List<String> targetPath) {
  Navigator.of(context).popUntil((route) => route.isFirst);
  AppRoutes.openFolder(context, targetPath);
}

Future<void> _internalCopyFlow(
  BuildContext context,
  List<String> targetPath,
  RemoteFileRef file,
) async {
  final bool ok;
  try {
    ok = await copyRemoteFileTo(
      context: context,
      source: file,
      targetFolderPath: targetPath.join('/'),
    );
  } catch (e) {
    if (context.mounted) {
      InfoDialog.show(
        context,
        errorToUserMessage(e),
        title: 'Kopieren fehlgeschlagen',
        copyable: true,
      );
    }
    return;
  }
  if (!ok || !context.mounted) return;
  _finishWithFolder(context, targetPath);
}
