import 'dart:async';

import 'package:flutter/material.dart';

import '../../routing/app_routes.dart';
import '../../utils/downloads/download_job.dart';
import '../../utils/downloads/download_manager.dart';
import '../../utils/haptics.dart';
import '../../view/pages/files/data/file_type_icon.dart';
import '../centered_leading.dart';
import '../details_bottom_sheet.dart';
import '../info_dialog.dart';
import 'stale_download_guard.dart';

/// Overview of all active and finished-but-unopened downloads. Lets the user
/// open/switch between finished files, cancel running ones and retry failures.
/// Completes when the sheet is dismissed.
Future<void> showDownloadsSheet(BuildContext context) {
  final rootContext = context;
  return showDetailsBottomSheet(
    context,
    header: const Padding(
      padding: EdgeInsets.fromLTRB(16, 4, 16, 12),
      child: Text(
        'Downloads',
        style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
      ),
    ),
    children: (sheetContext) => [
      _DownloadsList(rootContext: rootContext, sheetContext: sheetContext),
    ],
  );
}

class _DownloadsList extends StatefulWidget {
  const _DownloadsList({required this.rootContext, required this.sheetContext});

  /// Context under the navigator, used to push the viewer after the sheet closes.
  final BuildContext rootContext;
  final BuildContext sheetContext;

  @override
  State<_DownloadsList> createState() => _DownloadsListState();
}

class _DownloadsListState extends State<_DownloadsList> {
  ModalRoute<dynamic>? _sheetRoute;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _sheetRoute = ModalRoute.of(context);
  }

  @override
  Widget build(BuildContext context) =>
      ValueListenableBuilder<List<DownloadJob>>(
        valueListenable: DownloadManager.instance.visibleJobs,
        builder: (context, jobs, _) {
          if (jobs.isEmpty) {
            // Nothing left to manage — close the sheet, but only if it's still
            // the top route. When _open drained the tray it already popped the
            // sheet and pushed the viewer; without this guard we'd pop that
            // viewer straight back off.
            WidgetsBinding.instance.addPostFrameCallback((_) {
              final route = _sheetRoute;
              if (route != null && route.isCurrent) {
                Navigator.of(widget.sheetContext).pop();
              }
            });
            return const SizedBox.shrink();
          }
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              for (final job in jobs)
                _DownloadRow(
                  key: ValueKey(job.remotePath),
                  job: job,
                  onOpen: () => _open(job),
                ),
            ],
          );
        },
      );

  void _open(DownloadJob job) {
    final path = job.localPath;
    if (path == null) return;
    // Pop the sheet first so a stale-file dialog lands on the underlying
    // screen instead of an emptied sheet.
    Navigator.of(widget.sheetContext).pop();
    if (!ensureDownloadStillExists(widget.rootContext, job)) return;
    Haptics.success();
    DownloadManager.instance.markOpened(job);
    AppRoutes.openFileViewer(
      widget.rootContext,
      path,
      remoteFile: job.remoteFile,
    );
  }
}

class _DownloadRow extends StatelessWidget {
  const _DownloadRow({required this.job, required this.onOpen, super.key});

  final DownloadJob job;
  final VoidCallback onOpen;

  @override
  Widget build(BuildContext context) => AnimatedBuilder(
    animation: job.status,
    builder: (context, _) {
      final status = job.status.value;
      final theme = Theme.of(context);

      final tile = ListTile(
        leading: CenteredLeading(Icon(iconForFileName(job.name))),
        title: Text(job.name, maxLines: 2, overflow: TextOverflow.ellipsis),
        subtitle: switch (status) {
          DownloadInProgress(:final percent) => Row(
            children: [
              Expanded(
                child: LinearProgressIndicator(
                  value: percent <= 0 ? null : percent / 100,
                ),
              ),
              const SizedBox(width: 10),
              Text('${percent.round()}%'),
            ],
          ),
          DownloadDone() => const Text('Fertig – tippen zum Öffnen'),
          DownloadFailed() => Text(
            'Fehlgeschlagen',
            style: TextStyle(color: theme.colorScheme.error),
          ),
          DownloadCancelled() => const Text('Abgebrochen'),
        },
        trailing: switch (status) {
          DownloadInProgress() => IconButton(
            icon: const Icon(Icons.close),
            tooltip: 'Abbrechen',
            onPressed: () =>
                unawaited(DownloadManager.instance.cancel(job)),
          ),
          DownloadFailed() => IconButton(
            icon: const Icon(Icons.refresh),
            tooltip: 'Erneut versuchen',
            onPressed: () => unawaited(DownloadManager.instance.retry(job)),
          ),
          DownloadDone() => const Icon(Icons.open_in_new),
          DownloadCancelled() => null,
        },
        onTap: switch (status) {
          DownloadDone() => onOpen,
          DownloadFailed() => () => _showError(context, status),
          _ => null,
        },
      );

      // Finished rows can be swiped away; running ones stay put.
      if (status is DownloadInProgress) return tile;
      return Dismissible(
        key: ValueKey('dismiss-${job.remotePath}'),
        direction: DismissDirection.endToStart,
        background: Container(
          alignment: Alignment.centerRight,
          padding: const EdgeInsets.only(right: 24),
          color: theme.colorScheme.errorContainer,
          child: Icon(Icons.delete_outline, color: theme.colorScheme.onErrorContainer),
        ),
        onDismissed: (_) => DownloadManager.instance.dismiss(job),
        child: tile,
      );
    },
  );

  void _showError(BuildContext context, DownloadFailed status) {
    InfoDialog.show(
      context,
      status.message,
      title: 'Download fehlgeschlagen',
      copyable: true,
    );
  }
}
