import 'package:flutter/material.dart';
import 'package:nextcloud/nextcloud.dart';

import '../../../../api/marianumcloud/webdav/queries/list_files/list_files_cache.dart';
import '../../../../api/marianumcloud/webdav/webdav_api.dart';
import '../../../../utils/file_clipboard.dart';
import '../../../../widget/info_dialog.dart';

/// Banner that appears at the top of a Files folder while there is something
/// in the file clipboard. Shows the cut/copy state and offers a "Hier
/// einfügen" button.
class ClipboardBanner extends StatefulWidget {
  final String currentFolder;
  final VoidCallback onPasteDone;

  const ClipboardBanner({
    required this.currentFolder,
    required this.onPasteDone,
    super.key,
  });

  @override
  State<ClipboardBanner> createState() => _ClipboardBannerState();
}

class _ClipboardBannerState extends State<ClipboardBanner> {
  bool _busy = false;

  // All paths here are relative to the WebDAV root (matching `CacheableFile.path`).
  // Root is the empty string ''. Folders end with '/'.
  String _normalised(String path) {
    final stripped = path.replaceAll(RegExp(r'^/+|/+$'), '');
    return stripped.isEmpty ? '' : '$stripped/';
  }

  String _joinPath(String folder, String name, {required bool isDirectory}) =>
      isDirectory ? '$folder$name/' : '$folder$name';

  // Disabled when:
  // - clipboard is empty
  // - we'd be pasting a folder into itself or one of its descendants
  // - every entry already lives in the current folder (paste would be a no-op)
  bool get _canPaste {
    final cb = FileClipboard.instance;
    if (cb.isEmpty) return false;
    final dst = _normalised(widget.currentFolder);
    var atLeastOneActionable = false;
    for (final f in cb.files) {
      if (f.isDirectory) {
        final src = _normalised(f.path);
        if (dst == src || dst.startsWith(src)) return false;
      }
      final destination = _joinPath(
        widget.currentFolder,
        f.name,
        isDirectory: f.isDirectory,
      );
      if (destination != f.path) atLeastOneActionable = true;
    }
    return atLeastOneActionable;
  }

  // Cache key format used by ListFilesCache (matches FilesBloc's pathString:
  // relative, no leading or trailing slash; root is '/').
  String _parentCacheKey(String relativePath) {
    final stripped = relativePath.replaceAll(RegExp(r'^/+|/+$'), '');
    if (!stripped.contains('/')) return '/';
    final parts = stripped.split('/')..removeLast();
    return parts.isEmpty ? '/' : parts.join('/');
  }

  Future<void> _paste() async {
    final cb = FileClipboard.instance;
    if (_busy || !_canPaste) return;
    setState(() => _busy = true);
    final operation = cb.operation;
    final errors = <String>[];
    final invalidatedSourceFolders = <String>{};
    try {
      final webdav = await WebdavApi.webdav;
      for (final file in cb.files) {
        final destination = _joinPath(
          widget.currentFolder,
          file.name,
          isDirectory: file.isDirectory,
        );
        if (destination == file.path) continue;
        try {
          if (operation == FileClipboardOperation.cut) {
            await webdav.move(
              PathUri.parse(file.path),
              PathUri.parse(destination),
            );
            invalidatedSourceFolders.add(_parentCacheKey(file.path));
          } else {
            await webdav.copy(
              PathUri.parse(file.path),
              PathUri.parse(destination),
            );
          }
        } on Object catch (e) {
          errors.add('${file.name}: $e');
        }
      }
      // After cut, the source folders no longer contain the moved files. Drop
      // their cached listings so the next visit fetches fresh data instead of
      // briefly showing the moved file as still present.
      for (final folder in invalidatedSourceFolders) {
        await ListFilesCache.invalidate(folder);
      }
      if (operation == FileClipboardOperation.cut) cb.clear();
      widget.onPasteDone();
    } finally {
      if (mounted) setState(() => _busy = false);
    }
    if (errors.isNotEmpty && mounted) {
      InfoDialog.show(
        context,
        errors.join('\n\n'),
        copyable: true,
        title: 'Einfügen teilweise fehlgeschlagen',
      );
    }
  }

  @override
  Widget build(BuildContext context) => ListenableBuilder(
    listenable: FileClipboard.instance,
    builder: (context, _) {
      final cb = FileClipboard.instance;
      if (cb.isEmpty) return const SizedBox.shrink();
      final cut = cb.operation == FileClipboardOperation.cut;
      final count = cb.files.length;
      final label = count == 1 ? '"${cb.files.first.name}"' : '$count Elemente';
      return Material(
        color: Theme.of(context).colorScheme.secondaryContainer,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
          child: Row(
            children: [
              Icon(
                cut ? Icons.drive_file_move_outline : Icons.copy_outlined,
                size: 20,
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Text(
                  cut ? '$label verschieben' : '$label kopieren',
                  overflow: TextOverflow.ellipsis,
                ),
              ),
              TextButton(
                onPressed: _busy || !_canPaste ? null : _paste,
                child: _busy
                    ? const SizedBox(
                        width: 14,
                        height: 14,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                    : const Text('Hier einfügen'),
              ),
              IconButton(
                tooltip: 'Verwerfen',
                icon: const Icon(Icons.close, size: 20),
                onPressed: _busy ? null : cb.clear,
              ),
            ],
          ),
        ),
      );
    },
  );
}
