import 'dart:io';
import 'package:flutter/material.dart';
import '../constants/mpob_colors.dart';
import '../widgets/palm_bounding_box.dart';
import '../widgets/class_picker_sheet.dart';
import '../services/app_strings.dart';
import '../theme/locale_controller.dart';
import 'record_detail_screen.dart' show DetectionData;

enum _HandlePosition { topLeft, topRight, bottomLeft, bottomRight }

class _DraftBox {
  Rect normalizedBox;
  String? className;
  _DraftBox({required this.normalizedBox});

  _DraftBox copy() => _DraftBox(normalizedBox: normalizedBox)..className = className;
}

/// Full-screen editor for adding bunches the AI missed. Existing detections
/// are shown dimmed for spatial reference only — geometry edits here apply
/// exclusively to newly-placed boxes; nothing about the AI's own boxes is
/// changed. Nothing persists until the checkmark is tapped.
class DetectionEditorScreen extends StatefulWidget {
  final String imagePath;
  final List<DetectionData> existingDetections;

  const DetectionEditorScreen({super.key, required this.imagePath, required this.existingDetections});

  @override
  State<DetectionEditorScreen> createState() => _DetectionEditorScreenState();
}

class _DetectionEditorScreenState extends State<DetectionEditorScreen> {
  static const double _defaultBoxSize = 0.15;
  static const double _minBoxSize = 0.03;

  final List<_DraftBox> _draftBoxes = [];
  final List<List<_DraftBox>> _undoStack = [];
  final List<List<_DraftBox>> _redoStack = [];

  bool _showReference = true;
  int? _selectedIndex;
  int? _resizingIndex;

  bool get _canSave => _draftBoxes.isNotEmpty && _draftBoxes.every((b) => b.className != null);
  bool get _canUndo => _undoStack.isNotEmpty;
  bool get _canRedo => _redoStack.isNotEmpty;

  List<_DraftBox> _snapshot() => _draftBoxes.map((b) => b.copy()).toList();

  /// Records the current state onto the undo stack before a mutation.
  /// Must be called before the mutating statement, inside the same
  /// [setState] closure, so the snapshot captures the pre-mutation state.
  void _pushHistory() {
    _undoStack.add(_snapshot());
    _redoStack.clear();
  }

  void _undo() {
    if (!_canUndo) return;
    setState(() {
      _redoStack.add(_snapshot());
      final prev = _undoStack.removeLast();
      _draftBoxes
        ..clear()
        ..addAll(prev);
      _selectedIndex = null;
    });
  }

  void _redo() {
    if (!_canRedo) return;
    setState(() {
      _undoStack.add(_snapshot());
      final next = _redoStack.removeLast();
      _draftBoxes
        ..clear()
        ..addAll(next);
      _selectedIndex = null;
    });
  }

  Future<void> _pickClassFor(int index) async {
    final result = await showClassPickerSheet(context, currentClass: _draftBoxes[index].className);
    if (result != null && result.className != null && mounted) {
      setState(() {
        _pushHistory();
        _draftBoxes[index].className = result.className;
      });
    }
  }

  void _placeBoxAt(Offset normalizedCenter) {
    const half = _defaultBoxSize / 2;
    final left = (normalizedCenter.dx - half).clamp(0.0, 1.0 - _defaultBoxSize);
    final top = (normalizedCenter.dy - half).clamp(0.0, 1.0 - _defaultBoxSize);
    final rect = Rect.fromLTWH(left, top, _defaultBoxSize, _defaultBoxSize);
    final newIndex = _draftBoxes.length;
    setState(() {
      _pushHistory();
      _draftBoxes.add(_DraftBox(normalizedBox: rect));
      _selectedIndex = newIndex;
    });
    _pickClassFor(newIndex);
  }

  /// Every tap on empty canvas places a new box — placing is the sole
  /// purpose of this screen, so there's no separate "Add" mode to enter.
  void _handleCanvasTap(Offset localPosition, BoxConstraints constraints) {
    final normalized = Offset(
      (localPosition.dx / constraints.maxWidth).clamp(0.0, 1.0),
      (localPosition.dy / constraints.maxHeight).clamp(0.0, 1.0),
    );
    _placeBoxAt(normalized);
  }

  void _moveBox(int index, Offset normalizedDelta) {
    setState(() {
      final box = _draftBoxes[index];
      final w = box.normalizedBox.width;
      final h = box.normalizedBox.height;
      final left = (box.normalizedBox.left + normalizedDelta.dx).clamp(0.0, 1.0 - w);
      final top = (box.normalizedBox.top + normalizedDelta.dy).clamp(0.0, 1.0 - h);
      box.normalizedBox = Rect.fromLTWH(left, top, w, h);
    });
  }

  void _resizeBox(int index, _HandlePosition handle, Offset normalizedDelta) {
    setState(() {
      final box = _draftBoxes[index];
      final rect = box.normalizedBox;
      double left = rect.left, top = rect.top, right = rect.right, bottom = rect.bottom;
      switch (handle) {
        case _HandlePosition.topLeft:
          left = (left + normalizedDelta.dx).clamp(0.0, right - _minBoxSize);
          top = (top + normalizedDelta.dy).clamp(0.0, bottom - _minBoxSize);
          break;
        case _HandlePosition.topRight:
          right = (right + normalizedDelta.dx).clamp(left + _minBoxSize, 1.0);
          top = (top + normalizedDelta.dy).clamp(0.0, bottom - _minBoxSize);
          break;
        case _HandlePosition.bottomLeft:
          left = (left + normalizedDelta.dx).clamp(0.0, right - _minBoxSize);
          bottom = (bottom + normalizedDelta.dy).clamp(top + _minBoxSize, 1.0);
          break;
        case _HandlePosition.bottomRight:
          right = (right + normalizedDelta.dx).clamp(left + _minBoxSize, 1.0);
          bottom = (bottom + normalizedDelta.dy).clamp(top + _minBoxSize, 1.0);
          break;
      }
      box.normalizedBox = Rect.fromLTRB(left, top, right, bottom);
    });
  }

  void _deleteBox(int index) {
    setState(() {
      _pushHistory();
      _draftBoxes.removeAt(index);
      _selectedIndex = null;
    });
  }

  void _save() {
    final result = _draftBoxes
        .map((b) => DetectionData(className: b.className!, confidence: 1.0, normalizedBox: b.normalizedBox, isManual: true))
        .toList();
    Navigator.pop(context, result);
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<String>(
      valueListenable: LocaleController.localeCode,
      builder: (context, _, _) => _buildScaffold(context),
    );
  }

  Widget _buildScaffold(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: Text(AppStrings.t('add_missed_bunch_button')),
        actions: [
          IconButton(
            tooltip: AppStrings.t('undo_tooltip'),
            onPressed: _canUndo ? _undo : null,
            icon: const Icon(Icons.undo),
          ),
          IconButton(
            tooltip: AppStrings.t('redo_tooltip'),
            onPressed: _canRedo ? _redo : null,
            icon: const Icon(Icons.redo),
          ),
          IconButton(
            tooltip: _showReference ? AppStrings.t('hide_reference_tooltip') : AppStrings.t('show_reference_tooltip'),
            onPressed: () => setState(() => _showReference = !_showReference),
            icon: Icon(_showReference ? Icons.visibility_outlined : Icons.visibility_off_outlined),
          ),
        ],
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
            child: Text(
              AppStrings.t('editor_instruction_text'),
              style: TextStyle(fontSize: 13, color: colorScheme.onSurface.withValues(alpha: 0.7)),
            ),
          ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              child: AspectRatio(
                aspectRatio: 1,
                child: Container(
                  decoration: BoxDecoration(
                    color: colorScheme.surfaceContainerHighest,
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: widget.imagePath.isNotEmpty && File(widget.imagePath).existsSync()
                      ? ClipRRect(
                          borderRadius: BorderRadius.circular(12),
                          child: LayoutBuilder(
                            builder: (context, constraints) {
                              return GestureDetector(
                                behavior: HitTestBehavior.opaque,
                                onTapUp: (details) => _handleCanvasTap(details.localPosition, constraints),
                                child: Stack(
                                  children: [
                                    Positioned.fill(child: Image.file(File(widget.imagePath), fit: BoxFit.contain)),
                                    if (_showReference)
                                      ...widget.existingDetections.map((d) => PalmBoundingBox(
                                            normalizedRect: d.normalizedBox,
                                            label: d.className,
                                            confidence: d.confidence,
                                            constraints: constraints,
                                            opacity: 0.35,
                                          )),
                                    ..._draftBoxes.asMap().entries.map((entry) {
                                      final i = entry.key;
                                      return _DraggableBox(
                                        box: entry.value,
                                        constraints: constraints,
                                        isSelected: _selectedIndex == i,
                                        isResizing: _resizingIndex == i,
                                        onSelect: () => setState(() => _selectedIndex = i),
                                        onMoveStart: () => setState(_pushHistory),
                                        onMove: (delta) => _moveBox(i, delta),
                                        onResizeStart: () => setState(() {
                                          _pushHistory();
                                          _resizingIndex = i;
                                        }),
                                        onResize: (handle, delta) => _resizeBox(i, handle, delta),
                                        onResizeEnd: () => setState(() => _resizingIndex = null),
                                      );
                                    }),
                                  ],
                                ),
                              );
                            },
                          ),
                        )
                      : Center(child: Icon(Icons.image, size: 64, color: colorScheme.onSurface.withValues(alpha: 0.4))),
                ),
              ),
            ),
          ),
          if (_selectedIndex != null) _buildSelectedBoxToolbar(colorScheme),
          _buildBottomBar(colorScheme),
        ],
      ),
    );
  }

  Widget _buildSelectedBoxToolbar(ColorScheme colorScheme) {
    final box = _draftBoxes[_selectedIndex!];
    return Padding(
      padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
      child: Row(
        children: [
          Expanded(
            child: OutlinedButton.icon(
              onPressed: () => _pickClassFor(_selectedIndex!),
              icon: Icon(
                Icons.circle,
                size: 12,
                color: box.className == null ? colorScheme.onSurface.withValues(alpha: 0.4) : getMPOBColor(box.className!),
              ),
              label: Text(box.className == null ? AppStrings.t('pick_grade_placeholder') : AppStrings.className(box.className!)),
            ),
          ),
          const SizedBox(width: 8),
          IconButton(
            onPressed: () => _deleteBox(_selectedIndex!),
            icon: Icon(Icons.delete_outline, color: colorScheme.error),
          ),
        ],
      ),
    );
  }

  Widget _buildBottomBar(ColorScheme colorScheme) {
    final count = _draftBoxes.length;
    return SafeArea(
      top: false,
      child: Padding(
        padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
        child: Row(
          children: [
            IconButton.filled(
              onPressed: () => Navigator.pop(context),
              style: IconButton.styleFrom(
                backgroundColor: colorScheme.surfaceContainerHighest,
                foregroundColor: colorScheme.onSurface,
                minimumSize: const Size(52, 52),
              ),
              icon: const Icon(Icons.close),
            ),
            Expanded(
              child: Text(
                count == 0
                    ? AppStrings.t('editor_tap_to_add_hint')
                    : AppStrings.tp(count == 1 ? 'editor_bunches_added_singular' : 'editor_bunches_added_plural', {'count': '$count'}),
                textAlign: TextAlign.center,
                style: TextStyle(fontSize: 13, color: colorScheme.onSurface.withValues(alpha: 0.7)),
              ),
            ),
            IconButton.filled(
              onPressed: _canSave ? _save : null,
              style: IconButton.styleFrom(
                backgroundColor: colorScheme.primary,
                foregroundColor: colorScheme.onPrimary,
                disabledBackgroundColor: colorScheme.onSurface.withValues(alpha: 0.12),
                minimumSize: const Size(52, 52),
              ),
              icon: const Icon(Icons.check),
            ),
          ],
        ),
      ),
    );
  }
}

class _DraggableBox extends StatelessWidget {
  final _DraftBox box;
  final BoxConstraints constraints;
  final bool isSelected;
  final bool isResizing;
  final VoidCallback onSelect;
  final VoidCallback onMoveStart;
  final ValueChanged<Offset> onMove;
  final VoidCallback onResizeStart;
  final void Function(_HandlePosition handle, Offset delta) onResize;
  final VoidCallback onResizeEnd;

  const _DraggableBox({
    required this.box,
    required this.constraints,
    required this.isSelected,
    required this.isResizing,
    required this.onSelect,
    required this.onMoveStart,
    required this.onMove,
    required this.onResizeStart,
    required this.onResize,
    required this.onResizeEnd,
  });

  static const double _handleSize = 20;

  @override
  Widget build(BuildContext context) {
    final rect = box.normalizedBox;
    final color = box.className == null ? Colors.grey : getMPOBColor(box.className!);

    return Positioned(
      left: rect.left * constraints.maxWidth,
      top: rect.top * constraints.maxHeight,
      width: rect.width * constraints.maxWidth,
      height: rect.height * constraints.maxHeight,
      child: Stack(
        clipBehavior: Clip.none,
        children: [
          GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: onSelect,
            onPanStart: (_) => onMoveStart(),
            onPanUpdate: (details) => onMove(
              Offset(details.delta.dx / constraints.maxWidth, details.delta.dy / constraints.maxHeight),
            ),
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(color: color, width: 3),
                borderRadius: BorderRadius.circular(4),
              ),
              child: box.className != null
                  ? Align(
                      alignment: Alignment.topLeft,
                      child: Container(
                        padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
                        color: color,
                        child: Text(
                          AppStrings.className(box.className!),
                          style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
                        ),
                      ),
                    )
                  : null,
            ),
          ),
          if (isSelected) ..._buildHandles(),
          if (isResizing)
            Positioned(
              top: -32,
              left: 0,
              right: 0,
              child: Center(
                child: Container(
                  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                  decoration: BoxDecoration(color: Colors.black87, borderRadius: BorderRadius.circular(12)),
                  child: Text(
                    "${(rect.width * 100).toStringAsFixed(0)}% × ${(rect.height * 100).toStringAsFixed(0)}%",
                    style: const TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.w600),
                  ),
                ),
              ),
            ),
        ],
      ),
    );
  }

  List<Widget> _buildHandles() {
    const half = _handleSize / 2;
    final specs = <_HandlePosition, Map<String, double>>{
      _HandlePosition.topLeft: {'left': -half, 'top': -half},
      _HandlePosition.topRight: {'right': -half, 'top': -half},
      _HandlePosition.bottomLeft: {'left': -half, 'bottom': -half},
      _HandlePosition.bottomRight: {'right': -half, 'bottom': -half},
    };
    return specs.entries.map((entry) {
      final pos = entry.value;
      return Positioned(
        left: pos['left'],
        right: pos['right'],
        top: pos['top'],
        bottom: pos['bottom'],
        child: GestureDetector(
          behavior: HitTestBehavior.opaque,
          onPanStart: (_) => onResizeStart(),
          onPanUpdate: (details) => onResize(
            entry.key,
            Offset(details.delta.dx / constraints.maxWidth, details.delta.dy / constraints.maxHeight),
          ),
          onPanEnd: (_) => onResizeEnd(),
          onPanCancel: onResizeEnd,
          child: Container(
            width: _handleSize,
            height: _handleSize,
            decoration: BoxDecoration(
              color: Colors.white,
              shape: BoxShape.circle,
              border: Border.all(color: Colors.black45, width: 2),
            ),
          ),
        ),
      );
    }).toList();
  }
}
