// lib/editor/canvas/pen_editor_screen.dart
//
// Page-based pen-first PDF editor. Opens a PDF with pdfrx's document API,
// shows ONE page at a time as a bitmap (PdfPageView — a per-page widget that
// renders to an image and does NOT capture pan/zoom gestures), overlaid by the
// ink layer. Both share one transform via PenCanvas. Prev/Next + jump-to-page.

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

import '../../services/database_service.dart';
import '../engine/stroke_geometry.dart' show kDefaultPenThinning;
import '../engine/stroke_model.dart';
import '../engine/undo_stack.dart';
import '../input/diagnostic_logger.dart';
import '../input/pen_config.dart';
import '../input/pen_input_service.dart';
import '../persistence/editor_repository.dart';
import '../persistence/save_scheduler.dart';
import '../ui/pen_settings_page.dart';
import '../ui/thumbnail_grid.dart';
import 'input_diagnostics.dart';
import 'pen_canvas.dart';
import 'pen_stroke.dart';

/// Stable deterministic document-id for a file path (djb2 hash → hex).
///
/// Produces a fixed-length hex string from the path so the id is filesystem-
/// independent (no slashes, spaces, or non-ASCII characters) and stable across
/// restarts. Collisions are astronomically unlikely for a single-user app.
String _documentIdFromPath(String path) {
  var hash = 5381;
  for (final c in path.codeUnits) {
    hash = ((hash << 5) + hash + c) & 0xFFFFFFFF;
  }
  return hash.toRadixString(16).padLeft(8, '0');
}

class PenEditorScreen extends StatefulWidget {
  const PenEditorScreen({super.key, required this.pdfPath});

  final String pdfPath;

  @override
  State<PenEditorScreen> createState() => _PenEditorScreenState();
}

class _PenEditorScreenState extends State<PenEditorScreen> {
  PdfDocument? _document;
  Object? _openError;

  /// 0-based current page index.
  int _pageIndex = 0;

  /// Strokes per page, keyed by 0-based page index (normalized coords).
  final Map<int, List<PenStroke>> _strokesByPage = {};

  /// Per-page undo/redo history. Snapshot-before-change discipline: the
  /// pre-mutation stroke list is recorded before each commit/erase.
  final Map<int, UndoStack<List<PenStroke>>> _undo = {};

  UndoStack<List<PenStroke>> _undoFor(int page) =>
      _undo.putIfAbsent(page, () => UndoStack<List<PenStroke>>());

  /// Pen input configuration (widths, finger drawing, button actions).
  /// Loaded asynchronously in initState; null until ready.
  ///
  /// NOTE: the side-button / eraser-end ACTION MAPPINGS (sideButton/eraserEnd)
  /// are persisted via this controller but NOT yet consumed here — they wire
  /// into the input arbiter in a later step. Only widths and fingerDrawing are
  /// consumed for now.
  PenConfigController? _penConfig;

  // ── Persistence ────────────────────────────────────────────────────────────

  /// Stable document-id derived from the PDF file path.
  late final String _documentId;

  SaveScheduler? _saveScheduler;

  /// One shared transform for the current page; recentred on page change so
  /// each page opens fit-to-view and centered.
  final TransformationController _transform = TransformationController();

  /// Set when the page must be (re)centered on the next layout pass.
  bool _needsCenter = true;

  /// Live page value while dragging the page slider (null when not dragging).
  double? _scrub;

  /// Whether the page-jump slider is expanded (NOT persistent — toggled by
  /// tapping the page label; collapses after a jump).
  bool _showSlider = false;

  /// Latest pen-event debug readout (kind/pressure/min/max) — shown only when
  /// the diagnostic toggle is on, to inspect what Windows delivers.
  String _penDebug = '';
  bool _showPenDebug = false;

  // Tool state.
  CanvasTool _tool = CanvasTool.pen;
  Color _color = Colors.black;
  bool _allowFingerDrawing = false;

  /// Pen width as a fraction of page width (base; pressure thins it down).
  static const double _penWidthFraction = 0.006;
  static const double _highlighterWidthFraction = 0.02;

  static const List<Color> _palette = [
    Colors.black,
    Colors.red,
    Colors.blue,
    Colors.green,
    Colors.orange,
  ];

  @override
  void initState() {
    super.initState();
    _documentId = _documentIdFromPath(widget.pdfPath);
    // Begin listening to the native Windows pen plugin (barrel/eraser/tilt).
    // No-op on platforms without the plugin (W3).
    PenInputService.instance.start();
    _initPersistence();
    _initPenConfig();
    _open();
  }

  Future<void> _initPenConfig() async {
    final controller = await PenConfigController.load();
    if (!mounted) {
      controller.dispose();
      return;
    }
    // Rebuild the editor when pen settings change (width, pressure
    // sensitivity, button mappings) so the live canvas reflects them.
    controller.addListener(_onPenConfigChanged);
    setState(() {
      _penConfig = controller;
      // Adopt the persisted finger-drawing preference as the initial local
      // toggle state. The local 🖐 toggle keeps working and stays in sync with
      // the controller (see _toggleFingerDrawing).
      _allowFingerDrawing = controller.value.fingerDrawing;
    });
  }

  void _onPenConfigChanged() {
    if (mounted) setState(() {});
  }

  Future<void> _initPersistence() async {
    final service = await DatabaseService.getInstance();
    if (!mounted) return;
    final repo = await EditorRepository.fromService(service);
    final scheduler = SaveScheduler(repo);
    if (!mounted) {
      scheduler.dispose();
      return;
    }
    _saveScheduler = scheduler;
    // Load any previously persisted strokes for this document.
    await _loadPersistedStrokes(repo);
  }

  /// Load all persisted strokes for [_documentId] and populate [_strokesByPage].
  Future<void> _loadPersistedStrokes(EditorRepository repo) async {
    final hosted = await repo.loadDocument(_documentId);
    if (!mounted) return;
    final loaded = <int, List<PenStroke>>{};
    for (final entry in hosted.entries) {
      final pageIndex = _pageIndexFromHostId(entry.key);
      if (pageIndex == null) continue;
      loaded[pageIndex] = entry.value
          .map((es) => PenStroke(
                points: es.points
                    .map((ep) => PenPoint(ep.x, ep.y, ep.pressure, tilt: ep.tilt))
                    .toList(),
                color: es.color,
                width: es.width,
                kind: es.tool == EditorTool.highlighter
                    ? PenStrokeKind.highlighter
                    : PenStrokeKind.pen,
              ))
          .toList();
    }
    if (loaded.isNotEmpty) {
      setState(() {
        for (final entry in loaded.entries) {
          _strokesByPage[entry.key] = entry.value;
        }
      });
    }
  }

  /// Extract the page index from a host_id of the form
  /// `"doc:<documentId>:page:<pageIndex>"`.
  int? _pageIndexFromHostId(String hostId) {
    const marker = ':page:';
    final idx = hostId.lastIndexOf(marker);
    if (idx == -1) return null;
    return int.tryParse(hostId.substring(idx + marker.length));
  }

  Future<void> _open() async {
    try {
      final doc = await PdfDocument.openFile(widget.pdfPath);
      if (!mounted) {
        doc.dispose();
        return;
      }
      setState(() => _document = doc);
    } catch (e) {
      if (mounted) setState(() => _openError = e);
    }
  }

  @override
  void dispose() {
    // Flush any pending scheduled saves before tearing down.
    final scheduler = _saveScheduler;
    if (scheduler != null) {
      scheduler.flush(); // fire-and-forget; DB write continues in isolate
      scheduler.dispose();
    }
    _document?.dispose();
    _transform.dispose();
    _penConfig?.dispose();
    PenInputService.instance.stop();
    DiagnosticLogger.instance.stop();
    super.dispose();
  }

  List<PenStroke> get _currentStrokes =>
      _strokesByPage.putIfAbsent(_pageIndex, () => <PenStroke>[]);

  void _commitStroke(PenStroke stroke) {
    // Snapshot-before-change: record the pre-mutation page state for undo.
    _undoFor(_pageIndex).record(List<PenStroke>.of(_currentStrokes));
    setState(() {
      // Replace with a NEW list so StaticInkPainter sees a fresh identity and
      // actually repaints (mutating in place would alias the old painter's list
      // and shouldRepaint would see no change → committed strokes vanish).
      _strokesByPage[_pageIndex] = [
        ...?_strokesByPage[_pageIndex],
        stroke,
      ];
    });
    // Snapshot SYNCHRONOUSLY (before any await) then schedule persistence.
    final snapshot = List<PenStroke>.of(_strokesByPage[_pageIndex]!);
    _schedulePageSave(_pageIndex, snapshot);
  }

  /// Replace committed stroke [index] with its surviving pieces after a partial
  /// (segment) erase. An empty [replacements] list removes the stroke entirely.
  void _eraseStroke(int index, List<PenStroke> replacements) {
    final list = _strokesByPage[_pageIndex];
    final willMutate = list != null && index >= 0 && index < list.length;
    if (willMutate) {
      // Snapshot-before-change: record the pre-mutation page state for undo.
      _undoFor(_pageIndex).record(List<PenStroke>.of(list));
    }
    setState(() {
      if (list != null && index >= 0 && index < list.length) {
        final next = List<PenStroke>.of(list)
          ..replaceRange(index, index + 1, replacements);
        _strokesByPage[_pageIndex] = next;
      }
    });
    // Snapshot SYNCHRONOUSLY after the mutation, then schedule persistence.
    final current = _strokesByPage[_pageIndex];
    final snapshot =
        current != null ? List<PenStroke>.of(current) : <PenStroke>[];
    _schedulePageSave(_pageIndex, snapshot);
  }

  /// Convert [strokes] to [EditorStroke]s and hand them to the save scheduler.
  ///
  /// Must be called synchronously (no await between the snapshot and this call)
  /// so the scheduler receives an immutable copy of the in-memory state.
  void _schedulePageSave(int pageIndex, List<PenStroke> strokes) {
    final scheduler = _saveScheduler;
    if (scheduler == null) return;
    final editorStrokes =
        strokes.map((s) => EditorStroke.fromPenStroke(s)).toList();
    scheduler.schedule(
      'page',
      EditorRepository.pageHostId(_documentId, pageIndex),
      editorStrokes,
    );
  }

  void _goToPage(int index) {
    final doc = _document;
    if (doc == null) return;
    final clamped = index.clamp(0, doc.pages.length - 1);
    if (clamped == _pageIndex) return;
    setState(() {
      _pageIndex = clamped;
      _needsCenter = true; // recenter the new page on next layout
    });
  }

  /// Undo the last draw/erase on the current page, restoring and persisting
  /// the previous snapshot.
  void _performUndo() {
    final stack = _undoFor(_pageIndex);
    if (!stack.canUndo) return;
    final current = List<PenStroke>.of(_currentStrokes);
    final snapshot = stack.undo(current);
    if (snapshot == null) return;
    setState(() {
      // New list identity so StaticInkPainter repaints.
      _strokesByPage[_pageIndex] = List<PenStroke>.of(snapshot);
    });
    _schedulePageSave(_pageIndex, List<PenStroke>.of(snapshot));
  }

  /// Redo the last undone draw/erase on the current page.
  void _performRedo() {
    final stack = _undoFor(_pageIndex);
    if (!stack.canRedo) return;
    final snapshot = stack.redo();
    if (snapshot == null) return;
    setState(() {
      _strokesByPage[_pageIndex] = List<PenStroke>.of(snapshot);
    });
    _schedulePageSave(_pageIndex, List<PenStroke>.of(snapshot));
  }

  /// Cycle pen → highlighter → eraser → pen (for the toggleTool button action).
  void _cycleTool() {
    setState(() {
      _tool = switch (_tool) {
        CanvasTool.pen => CanvasTool.highlighter,
        CanvasTool.highlighter => CanvasTool.eraser,
        CanvasTool.eraser => CanvasTool.pen,
      };
    });
  }

  /// Handle a hardware pen-button action delivered by [PenCanvas] (W3).
  /// `eraser` and `pan` are handled inside the canvas; here we map the
  /// edge-triggered ones.
  void _handlePenButtonAction(PenButtonAction action) {
    switch (action) {
      case PenButtonAction.undo:
        _performUndo();
      case PenButtonAction.toggleTool:
        _cycleTool();
      case PenButtonAction.eraser:
      case PenButtonAction.pan:
      case PenButtonAction.none:
        break;
    }
  }

  /// Toggle finger-drawing, keeping the local state and the persisted config
  /// (when loaded) in sync.
  void _toggleFingerDrawing() {
    final next = !_allowFingerDrawing;
    setState(() => _allowFingerDrawing = next);
    _penConfig?.setFingerDrawing(next);
  }

  /// Open the page thumbnail grid; tapping a thumbnail navigates to that page.
  void _openThumbnails() {
    final doc = _document;
    if (doc == null) return;
    showPageThumbnailSheet(
      context,
      document: doc,
      currentPage: _pageIndex,
      onPageSelected: _goToPage,
    );
  }

  /// Open the pen settings sheet (widths, pressure, finger drawing, etc.).
  void _openPenSettings() {
    final config = _penConfig;
    if (config == null) return;
    showPenSettingsSheet(context, config);
  }

  /// Centre [pageSize] within [viewport] via the shared transform.
  void _centerPage(Size viewport, Size pageSize) {
    final tx = (viewport.width - pageSize.width) / 2;
    final ty = (viewport.height - pageSize.height) / 2;
    _transform.value = Matrix4.identity()..setTranslationRaw(tx, ty, 0);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Positioned.fill(child: _buildBody()),
          // Floating Material You tool palette (top-center).
          SafeArea(
            child: Align(
              alignment: Alignment.topCenter,
              child: Padding(
                padding: const EdgeInsets.only(top: 8),
                child: _buildToolPalette(),
              ),
            ),
          ),
          // Floating page-control pill (bottom-center).
          if (_document != null)
            SafeArea(
              child: Align(
                alignment: Alignment.bottomCenter,
                child: Padding(
                  padding: const EdgeInsets.only(bottom: 16),
                  child: _buildPagePill(),
                ),
              ),
            ),
          // Back button (top-left).
          SafeArea(
            child: Padding(
              padding: const EdgeInsets.all(8),
              child: _RoundIconButton(
                icon: Icons.arrow_back,
                tooltip: 'Back',
                onPressed: () => Navigator.of(context).maybePop(),
              ),
            ),
          ),
          // Pen diagnostic readout (top-right) — shows what Windows delivers.
          if (_showPenDebug)
            SafeArea(
              child: Align(
                alignment: Alignment.topRight,
                child: Padding(
                  padding: const EdgeInsets.all(8),
                  child: Material(
                    color: Theme.of(context).colorScheme.inverseSurface,
                    borderRadius: BorderRadius.circular(8),
                    child: ConstrainedBox(
                      constraints: const BoxConstraints(maxWidth: 380),
                      child: Padding(
                        padding: const EdgeInsets.symmetric(
                            horizontal: 10, vertical: 6),
                        child: ListenableBuilder(
                          listenable: InputDiagnostics.instance,
                          builder: (context, _) {
                            final cs = Theme.of(context).colorScheme;
                            final d = InputDiagnostics.instance;
                            final tail = d.trace.length > 6
                                ? d.trace.sublist(d.trace.length - 6)
                                : d.trace;
                            final mono = TextStyle(
                                fontFamily: 'monospace',
                                fontSize: 11,
                                color: cs.onInverseSurface);
                            final monoFaint = mono.copyWith(
                                fontSize: 10,
                                color:
                                    cs.onInverseSurface.withValues(alpha: 0.75));
                            return Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Text(
                                    _penDebug.isEmpty
                                        ? 'hover / draw with the pen…'
                                        : _penDebug,
                                    style: mono),
                                const SizedBox(height: 4),
                                Text(d.summary(), style: mono),
                                if (tail.isNotEmpty) ...[
                                  const SizedBox(height: 4),
                                  Text(tail.join('\n'), style: monoFaint),
                                ],
                                const SizedBox(height: 4),
                                Text(
                                    'log: ${DiagnosticLogger.instance.path ?? "(developer.log only)"}',
                                    style: monoFaint),
                                Align(
                                  alignment: Alignment.centerRight,
                                  child: TextButton(
                                    onPressed: () =>
                                        InputDiagnostics.instance.reset(),
                                    child: Text('Reset stats',
                                        style:
                                            TextStyle(color: cs.inversePrimary)),
                                  ),
                                ),
                              ],
                            );
                          },
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            ),
        ],
      ),
    );
  }

  Widget _buildBody() {
    if (_openError != null) {
      return Center(child: Text('Failed to open PDF:\n$_openError'));
    }
    final doc = _document;
    if (doc == null) {
      return const Center(child: CircularProgressIndicator());
    }
    if (doc.pages.isEmpty) {
      return const Center(child: Text('PDF has no pages.'));
    }

    final page = doc.pages[_pageIndex];

    return LayoutBuilder(
      builder: (context, constraints) {
        // Fit the page rectangle into the available viewport at scale 1.0; the
        // InteractiveViewer then zooms/pans from there. Ink normalized coords
        // map onto this rectangle.
        final fit = (constraints.maxWidth / page.width)
            .clamp(0.0, double.infinity);
        final fitH = constraints.maxHeight / page.height;
        final scale = fit < fitH ? fit : fitH;
        final pageSize = Size(page.width * scale, page.height * scale);

        if (_needsCenter) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (!mounted) return;
            _centerPage(
                Size(constraints.maxWidth, constraints.maxHeight), pageSize);
            setState(() => _needsCenter = false);
          });
        }
        return PenCanvas(
            key: ValueKey(_pageIndex),
            pageSize: pageSize,
            strokes: _currentStrokes,
            transformationController: _transform,
            tool: _tool,
            color: _color,
            strokeWidth: _tool == CanvasTool.highlighter
                ? (_penConfig?.value.highlighterWidth ??
                    _highlighterWidthFraction)
                : (_penConfig?.value.penWidth ?? _penWidthFraction),
            thinning:
                _penConfig?.value.pressureSensitivity ?? kDefaultPenThinning,
            sideButtonAction:
                _penConfig?.value.sideButton ?? PenButtonAction.eraser,
            eraserEndAction:
                _penConfig?.value.eraserEnd ?? PenButtonAction.eraser,
            onPenButtonAction: _handlePenButtonAction,
            allowFingerDrawing: _allowFingerDrawing,
            onPenDebug: _showPenDebug
                ? (s) => setState(() => _penDebug = s)
                : null,
            onStrokeComplete: _commitStroke,
            onEraseStroke: _eraseStroke,
            pageWidget: PdfPageView(
              document: doc,
              pageNumber: _pageIndex + 1,
              // Fill the SizedBox exactly so ink aligns to the page rect (no
              // internal letterboxing offset).
              pageSizeCallback: (biggest, page, rotation) => biggest,
              decoration: const BoxDecoration(color: Colors.white),
              backgroundColor: Colors.white,
            ),
          );
      },
    );
  }

  /// Floating Material You tool palette: a tonal rounded surface holding the
  /// tools, color dots, and finger-drawing toggle.
  Widget _buildToolPalette() {
    final cs = Theme.of(context).colorScheme;
    return Material(
      color: cs.surfaceContainerHigh,
      elevation: 3,
      borderRadius: BorderRadius.circular(28),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            _ToolButton(
              icon: Icons.edit_outlined,
              selected: _tool == CanvasTool.pen,
              tooltip: 'Pen',
              onPressed: () => setState(() => _tool = CanvasTool.pen),
            ),
            _ToolButton(
              icon: Icons.brush_outlined,
              selected: _tool == CanvasTool.highlighter,
              tooltip: 'Highlighter',
              onPressed: () => setState(() => _tool = CanvasTool.highlighter),
            ),
            _ToolButton(
              icon: Icons.cleaning_services_outlined,
              selected: _tool == CanvasTool.eraser,
              tooltip: 'Eraser',
              onPressed: () => setState(() => _tool = CanvasTool.eraser),
            ),
            _Divider(cs: cs),
            // Undo / redo (per page).
            _ToolButton(
              icon: Icons.undo,
              selected: false,
              tooltip: 'Undo',
              onPressed: _undoFor(_pageIndex).canUndo ? _performUndo : null,
            ),
            _ToolButton(
              icon: Icons.redo,
              selected: false,
              tooltip: 'Redo',
              onPressed: _undoFor(_pageIndex).canRedo ? _performRedo : null,
            ),
            _Divider(cs: cs),
            for (final c in _palette) _colorDot(c, cs),
            _Divider(cs: cs),
            _ToolButton(
              icon: _allowFingerDrawing ? Icons.touch_app : Icons.do_not_touch,
              selected: _allowFingerDrawing,
              tooltip: _allowFingerDrawing
                  ? 'Finger drawing ON'
                  : 'Finger drawing OFF (pen only)',
              onPressed: _toggleFingerDrawing,
            ),
            // Page thumbnail grid.
            _ToolButton(
              icon: Icons.grid_view,
              selected: false,
              tooltip: 'Pages',
              onPressed: _document != null ? _openThumbnails : null,
            ),
            // Pen settings.
            _ToolButton(
              icon: Icons.settings_outlined,
              selected: false,
              tooltip: 'Pen settings',
              onPressed: _penConfig != null ? _openPenSettings : null,
            ),
            _ToolButton(
              icon: Icons.bug_report_outlined,
              selected: _showPenDebug,
              tooltip: 'Input diagnostic (writes a log file)',
              onPressed: () {
                final on = !_showPenDebug;
                setState(() => _showPenDebug = on);
                if (on) {
                  InputDiagnostics.instance.reset();
                  DiagnosticLogger.instance.start();
                } else {
                  DiagnosticLogger.instance.stop();
                }
              },
            ),
          ],
        ),
      ),
    );
  }

  Widget _colorDot(Color c, ColorScheme cs) {
    final selected = _color == c;
    return GestureDetector(
      onTap: () => setState(() => _color = c),
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 150),
        width: 28,
        height: 28,
        margin: const EdgeInsets.symmetric(horizontal: 3),
        decoration: BoxDecoration(
          color: c,
          shape: BoxShape.circle,
          border: Border.all(
            color: selected ? cs.primary : cs.outlineVariant,
            width: selected ? 3 : 1.5,
          ),
        ),
      ),
    );
  }

  /// Floating page control: a COMPACT pill (prev / "n / total" / next). Tapping
  /// the label reveals a drag-slider — which is NOT persistent (collapses again
  /// on tap) so it doesn't block the page. No keyboard input (Windows IME is
  /// unreliable).
  Widget _buildPagePill() {
    final doc = _document!;
    final cs = Theme.of(context).colorScheme;
    final total = doc.pages.length;
    final shown = (_scrub ?? (_pageIndex + 1).toDouble()).round();
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        // Slider — shown only when expanded (not persistent).
        if (_showSlider && total > 1)
          Container(
            margin: const EdgeInsets.only(bottom: 8),
            constraints: const BoxConstraints(maxWidth: 420),
            child: Material(
              color: cs.surfaceContainerHigh,
              elevation: 3,
              borderRadius: BorderRadius.circular(28),
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 12),
                child: Slider(
                  min: 1,
                  max: total.toDouble(),
                  value: (_scrub ?? (_pageIndex + 1).toDouble())
                      .clamp(1, total.toDouble()),
                  label: '$shown',
                  divisions: total - 1,
                  onChanged: (v) => setState(() => _scrub = v),
                  onChangeEnd: (v) {
                    setState(() => _scrub = null);
                    _goToPage(v.round() - 1);
                  },
                ),
              ),
            ),
          ),
        // Compact pill — always; fits content (no big frame).
        Material(
          color: cs.surfaceContainerHigh,
          elevation: 3,
          borderRadius: BorderRadius.circular(28),
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                IconButton(
                  tooltip: 'Previous page',
                  icon: const Icon(Icons.chevron_left),
                  onPressed:
                      _pageIndex > 0 ? () => _goToPage(_pageIndex - 1) : null,
                ),
                TextButton(
                  onPressed: total > 1
                      ? () => setState(() => _showSlider = !_showSlider)
                      : null,
                  child: Text(
                    '$shown / $total',
                    style: TextStyle(
                      color: cs.onSurface,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                ),
                IconButton(
                  tooltip: 'Next page',
                  icon: const Icon(Icons.chevron_right),
                  onPressed: _pageIndex < total - 1
                      ? () => _goToPage(_pageIndex + 1)
                      : null,
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

/// A Material You toggle-style icon button for the tool palette.
class _ToolButton extends StatelessWidget {
  const _ToolButton({
    required this.icon,
    required this.selected,
    required this.tooltip,
    required this.onPressed,
  });

  final IconData icon;
  final bool selected;
  final String tooltip;

  /// Tap handler. When null the button renders disabled (dimmed, no ripple).
  final VoidCallback? onPressed;

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    final enabled = onPressed != null;
    final iconColor = !enabled
        ? cs.onSurfaceVariant.withValues(alpha: 0.38)
        : selected
            ? cs.onSecondaryContainer
            : cs.onSurfaceVariant;
    return Tooltip(
      message: tooltip,
      child: InkWell(
        borderRadius: BorderRadius.circular(20),
        onTap: onPressed,
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 150),
          margin: const EdgeInsets.symmetric(horizontal: 2),
          padding: const EdgeInsets.all(8),
          decoration: BoxDecoration(
            color: selected ? cs.secondaryContainer : Colors.transparent,
            borderRadius: BorderRadius.circular(20),
          ),
          child: Icon(
            icon,
            size: 22,
            color: iconColor,
          ),
        ),
      ),
    );
  }
}

class _Divider extends StatelessWidget {
  const _Divider({required this.cs});
  final ColorScheme cs;
  @override
  Widget build(BuildContext context) => Container(
        width: 1,
        height: 24,
        margin: const EdgeInsets.symmetric(horizontal: 6),
        color: cs.outlineVariant,
      );
}

/// A round, tonal icon button (used for the floating back button).
class _RoundIconButton extends StatelessWidget {
  const _RoundIconButton({
    required this.icon,
    required this.tooltip,
    required this.onPressed,
  });

  final IconData icon;
  final String tooltip;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    return Material(
      color: cs.surfaceContainerHigh,
      elevation: 3,
      shape: const CircleBorder(),
      child: IconButton(
        tooltip: tooltip,
        icon: Icon(icon),
        color: cs.onSurfaceVariant,
        onPressed: onPressed,
      ),
    );
  }
}
