import 'dart:io';
import 'dart:math' as math;

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_mlkit_object_detection/google_mlkit_object_detection.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:image/image.dart' as img;
import 'package:lottie/lottie.dart';
import 'package:path_provider/path_provider.dart';

import '../../../../core/services/vector_service.dart';
import 'camera_search.dart';

/// Interactive crop-confirmation screen shown after a photo is captured
/// or picked. Mirrors the Google Lens / Amazon "search by image" pattern:
///   1. The photo is shown with a draggable crop rectangle, pre-filled
///      with an ML Kit object-detection guess once it's ready.
///   2. The user can drag the four corner handles (or the box itself) to
///      adjust which region gets searched.
///   3. Tapping the confirm button crops the image, runs on-device OCR
///      on the cropped region to pull out any product text (brand,
///      model, etc.), sends both the image and text to the backend to
///      get vector embeddings, then shows an animated "Finding similar
///      products" sheet before navigating to the results screen.
class CropConfirmScreen extends StatefulWidget {
  final String imagePath;

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

  @override
  State<CropConfirmScreen> createState() => _CropConfirmScreenState();
}

enum _Corner { topLeft, topRight, bottomLeft, bottomRight }

class _CropConfirmScreenState extends State<CropConfirmScreen> {
  final ObjectDetector _objectDetector = ObjectDetector(
    options: ObjectDetectorOptions(
      mode: DetectionMode.single,
      classifyObjects: false,
      multipleObjects: true,
    ),
  );

  final TextRecognizer _textRecognizer = TextRecognizer();

  // Original decoded image dimensions (pixels).
  double? _imgWidth;
  double? _imgHeight;

  // Crop rectangle in *original image pixel* coordinates.
  Rect? _cropRect;

  bool _isDetecting = true;
  bool _isProcessing = false;

  // Updated every build so gesture callbacks can convert screen <-> image
  // coordinates using the current layout.
  double _scale = 1;
  double _offsetX = 0;
  double _offsetY = 0;

  @override
  void initState() {
    super.initState();
    _init();
  }

  @override
  void dispose() {
    _objectDetector.close();
    _textRecognizer.close();
    super.dispose();
  }

  Future<void> _init() async {
    final bytes = await File(widget.imagePath).readAsBytes();
    final decoded = img.decodeImage(bytes);
    if (decoded == null || !mounted) return;

    final w = decoded.width.toDouble();
    final h = decoded.height.toDouble();

    // Fallback: centered rect covering 80% of the image, used until (or
    // unless) object detection finds something.
    const inset = 0.1;
    final fallback = Rect.fromLTRB(
      w * inset,
      h * inset,
      w * (1 - inset),
      h * (1 - inset),
    );

    setState(() {
      _imgWidth = w;
      _imgHeight = h;
      _cropRect = fallback;
    });

    try {
      final inputImage = InputImage.fromFilePath(widget.imagePath);
      final objects = await _objectDetector.processImage(inputImage);
      if (!mounted || objects.isEmpty) {
        setState(() => _isDetecting = false);
        return;
      }
      objects.sort((a, b) {
        final areaA = a.boundingBox.width * a.boundingBox.height;
        final areaB = b.boundingBox.width * b.boundingBox.height;
        return areaB.compareTo(areaA);
      });
      final box = objects.first.boundingBox;
      setState(() {
        _cropRect = Rect.fromLTRB(
          box.left.clamp(0, w),
          box.top.clamp(0, h),
          box.right.clamp(0, w),
          box.bottom.clamp(0, h),
        );
        _isDetecting = false;
      });
    } catch (_) {
      if (mounted) setState(() => _isDetecting = false);
    }
  }

  double get _minCropSize {
    if (_imgWidth == null || _imgHeight == null) return 40;
    return math.min(_imgWidth!, _imgHeight!) * 0.12;
  }

  void _updateCorner(_Corner corner, Offset screenDelta) {
    if (_cropRect == null) return;
    final dx = screenDelta.dx / _scale;
    final dy = screenDelta.dy / _scale;
    final w = _imgWidth!;
    final h = _imgHeight!;
    final min = _minCropSize;

    Rect r = _cropRect!;
    switch (corner) {
      case _Corner.topLeft:
        final newLeft = (r.left + dx).clamp(0.0, r.right - min);
        final newTop = (r.top + dy).clamp(0.0, r.bottom - min);
        r = Rect.fromLTRB(newLeft, newTop, r.right, r.bottom);
        break;
      case _Corner.topRight:
        final newRight = (r.right + dx).clamp(r.left + min, w);
        final newTop = (r.top + dy).clamp(0.0, r.bottom - min);
        r = Rect.fromLTRB(r.left, newTop, newRight, r.bottom);
        break;
      case _Corner.bottomLeft:
        final newLeft = (r.left + dx).clamp(0.0, r.right - min);
        final newBottom = (r.bottom + dy).clamp(r.top + min, h);
        r = Rect.fromLTRB(newLeft, r.top, r.right, newBottom);
        break;
      case _Corner.bottomRight:
        final newRight = (r.right + dx).clamp(r.left + min, w);
        final newBottom = (r.bottom + dy).clamp(r.top + min, h);
        r = Rect.fromLTRB(r.left, r.top, newRight, newBottom);
        break;
    }
    setState(() => _cropRect = r);
  }

  void _moveRect(Offset screenDelta) {
    if (_cropRect == null) return;
    final dx = screenDelta.dx / _scale;
    final dy = screenDelta.dy / _scale;
    final w = _imgWidth!;
    final h = _imgHeight!;
    Rect r = _cropRect!.shift(Offset(dx, dy));

    // Clamp so the rect stays fully inside the image bounds.
    if (r.left < 0) r = r.shift(Offset(-r.left, 0));
    if (r.top < 0) r = r.shift(Offset(0, -r.top));
    if (r.right > w) r = r.shift(Offset(w - r.right, 0));
    if (r.bottom > h) r = r.shift(Offset(0, h - r.bottom));
    setState(() => _cropRect = r);
  }

  Future<void> _confirmCrop() async {
    if (_cropRect == null || _isProcessing) return;
    setState(() => _isProcessing = true);

    try {
      final bytes = await File(widget.imagePath).readAsBytes();
      final original = img.decodeImage(bytes);
      if (original == null) throw Exception('Could not decode image');

      final r = _cropRect!;
      final x = r.left.toInt().clamp(0, original.width - 1);
      final y = r.top.toInt().clamp(0, original.height - 1);
      final cw = r.width.toInt().clamp(1, original.width - x);
      final ch = r.height.toInt().clamp(1, original.height - y);

      final cropped = img.copyCrop(original, x: x, y: y, width: cw, height: ch);
      final croppedBytes = img.encodeJpg(cropped, quality: 92);

      final dir = await getTemporaryDirectory();
      final croppedPath =
          '${dir.path}/cropped_${DateTime.now().millisecondsSinceEpoch}.jpg';
      await File(croppedPath).writeAsBytes(croppedBytes);

      // Run on-device text recognition on the cropped region only, so we
      // pick up whatever brand/model/label text is inside the user's box.
      final extractedText = await _recognizeText(croppedPath);

      // Send the cropped image + extracted text to the backend, which
      // converts each into a vector embedding and returns both.
      VectorEmbeddingResult? embeddings;
      try {
        embeddings = await VectorService.getEmbeddings(
          imagePath: croppedPath,
          labelText: extractedText,
        );
        debugPrint(
            'Image vector (${embeddings.imageVector.length} dims): ${embeddings.imageVector}');
        debugPrint(
            'Text vector (${embeddings.textVector.length} dims): ${embeddings.textVector}');
      } catch (e) {
        debugPrint('Vector embedding failed: $e');
        // decide whether to continue to results without vectors, or bail out
      }

      // Give the "Finding similar products" sheet a moment to be visible
      // before navigating — this is where a real search API call would
      // happen instead of a fixed delay.
      await Future.delayed(const Duration(milliseconds: 1400));
      if (!mounted) return;

      Navigator.of(context).pushReplacement(
        MaterialPageRoute(
          builder: (_) => SearchResultsScreen(
            imagePath: croppedPath,
            wasDetected: true,
            extractedText: extractedText,
          ),
        ),
      );
    } catch (e) {
      if (mounted) {
        setState(() => _isProcessing = false);
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Could not crop photo: $e')),
        );
      }
    }
  }

  /// Runs ML Kit text recognition on [path] and returns the recognized
  /// text (empty string if nothing was found or recognition failed).
  Future<String> _recognizeText(String path) async {
    try {
      final inputImage = InputImage.fromFilePath(path);
      final result = await _textRecognizer.processImage(inputImage);
      return result.text.trim();
    } catch (_) {
      return '';
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF15141B),
      body: SafeArea(
        child: Column(
          children: [
            _buildTopBar(),
            const SizedBox(height: 8),
            Expanded(child: _buildImageArea()),
            const SizedBox(height: 16),
            if (!_isProcessing) _buildHintPill(),
            const SizedBox(height: 16),
          ],
        ),
      ),
      floatingActionButton: _isProcessing
          ? null
          : FloatingActionButton(
        backgroundColor: Colors.white,
        onPressed: _confirmCrop,
        child: const Icon(Icons.check, color: Colors.black87),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
      bottomSheet: _isProcessing ? _buildProcessingSheet() : null,
    );
  }

  Widget _buildTopBar() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
      child: Row(
        children: [
          IconButton(
            icon: const Icon(Icons.arrow_back, color: Colors.white),
            onPressed: () => Navigator.of(context).maybePop(),
          ),
          const Spacer(),
          Row(
            mainAxisSize: MainAxisSize.min,
            children: const [
              Text(
                'Nearle',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 22,
                  fontWeight: FontWeight.w700,
                ),
              ),
              SizedBox(width: 6),
              Text(
                'ai',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 20,
                  fontStyle: FontStyle.italic,
                ),
              ),
              SizedBox(width: 2),
              Icon(Icons.auto_awesome, color: Colors.white, size: 14),
            ],
          ),
          const Spacer(),
          const SizedBox(width: 48), // balances the back button
        ],
      ),
    );
  }

  Widget _buildHintPill() {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
      decoration: BoxDecoration(
        color: Colors.black.withOpacity(0.4),
        borderRadius: BorderRadius.circular(24),
      ),
      child: Text(
        _isDetecting ? 'Detecting product…' : 'Drag corners to adjust',
        style: const TextStyle(color: Colors.white, fontSize: 13),
      ),
    );
  }

  Widget _buildImageArea() {
    if (_imgWidth == null || _imgHeight == null || _cropRect == null) {
      return const Center(
        child: CircularProgressIndicator(color: Colors.white),
      );
    }

    return LayoutBuilder(
      builder: (context, constraints) {
        final availW = constraints.maxWidth;
        final availH = constraints.maxHeight;
        _scale = math.min(availW / _imgWidth!, availH / _imgHeight!);
        final dispW = _imgWidth! * _scale;
        final dispH = _imgHeight! * _scale;
        _offsetX = (availW - dispW) / 2;
        _offsetY = (availH - dispH) / 2;

        final r = _cropRect!;
        final screenRect = Rect.fromLTRB(
          _offsetX + r.left * _scale,
          _offsetY + r.top * _scale,
          _offsetX + r.right * _scale,
          _offsetY + r.bottom * _scale,
        );

        return Stack(
          children: [
            Positioned(
              left: _offsetX,
              top: _offsetY,
              width: dispW,
              height: dispH,
              child: Image.file(File(widget.imagePath), fit: BoxFit.fill),
            ),
            ..._buildDarkMask(screenRect, availW, availH),
            Positioned.fromRect(
              rect: screenRect,
              child: GestureDetector(
                behavior: HitTestBehavior.translucent,
                onPanUpdate: (d) => _moveRect(d.delta),
                child: Container(
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.white, width: 1.5),
                  ),
                ),
              ),
            ),
            ..._buildCornerHandles(screenRect),
          ],
        );
      },
    );
  }

  List<Widget> _buildDarkMask(Rect r, double availW, double availH) {
    const maskColor = Colors.black54;
    return [
      Positioned(
        left: 0,
        top: 0,
        right: 0,
        height: r.top.clamp(0, availH),
        child: IgnorePointer(child: Container(color: maskColor)),
      ),
      Positioned(
        left: 0,
        top: r.bottom,
        right: 0,
        bottom: 0,
        child: IgnorePointer(child: Container(color: maskColor)),
      ),
      Positioned(
        left: 0,
        top: r.top,
        width: r.left.clamp(0, availW),
        height: r.height,
        child: IgnorePointer(child: Container(color: maskColor)),
      ),
      Positioned(
        left: r.right,
        top: r.top,
        right: 0,
        height: r.height,
        child: IgnorePointer(child: Container(color: maskColor)),
      ),
    ];
  }

  List<Widget> _buildCornerHandles(Rect r) {
    return [
      _cornerHandle(r.topLeft, _Corner.topLeft),
      _cornerHandle(r.topRight, _Corner.topRight),
      _cornerHandle(r.bottomLeft, _Corner.bottomLeft),
      _cornerHandle(r.bottomRight, _Corner.bottomRight),
    ];
  }

  Widget _cornerHandle(Offset position, _Corner corner) {
    const hitSize = 44.0;
    return Positioned(
      left: position.dx - hitSize / 2,
      top: position.dy - hitSize / 2,
      width: hitSize,
      height: hitSize,
      child: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onPanUpdate: (d) => _updateCorner(corner, d.delta),
        child: Center(
          child: CustomPaint(
            size: const Size(28, 28),
            painter: _CornerBracketPainter(corner: corner),
          ),
        ),
      ),
    );
  }

  Widget _buildProcessingSheet() {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
      decoration: const BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.vertical(
          top: Radius.circular(28),
        ),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Container(
            width: 55,
            height: 5,
            decoration: BoxDecoration(
              color: Colors.grey.shade300,
              borderRadius: BorderRadius.circular(20),
            ),
          ),

          const SizedBox(height: 28),

          Container(
            height: 70,
            width: 70,
            decoration: const BoxDecoration(
              shape: BoxShape.circle,
            ),
            child: Center(
              child: SizedBox(
                width: 70,
                height: 70,
                child: Lottie.asset(
                  'assets/lotties/ai_loader.json',
                  height: 140,
                ),
              ),
            ),
          ),

          const SizedBox(height: 24),

          const Text(
            "Searching Similar Products",
            style: TextStyle(
              fontSize: 20,
              fontWeight: FontWeight.w700,
            ),
          ),

          const SizedBox(height: 10),

          Text(
            "Analyzing image, detecting text\nand finding the closest matches...",
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: 14,
              color: Colors.grey.shade600,
              height: 1.5,
            ),
          ),

          const SizedBox(height: 28),

          ClipRRect(
            borderRadius: BorderRadius.circular(20),
            child: const LinearProgressIndicator(
              minHeight: 8,
              backgroundColor: Color(0xffECECEC),
              color: Colors.green,
            ),
          ),

          const SizedBox(height: 14),

          Text(
            "This usually takes a few seconds",
            style: TextStyle(
              color: Colors.grey.shade500,
              fontSize: 12,
            ),
          ),
        ],
      ),
    );
  }
}

/// Draws a white L-shaped bracket oriented for the given corner, matching
/// the Google Lens style crop handles.
class _CornerBracketPainter extends CustomPainter {
  final _Corner corner;

  _CornerBracketPainter({required this.corner});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.white
      ..strokeWidth = 3
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke;

    final path = Path();
    switch (corner) {
      case _Corner.topLeft:
        path.moveTo(0, size.height);
        path.lineTo(0, 0);
        path.lineTo(size.width, 0);
        break;
      case _Corner.topRight:
        path.moveTo(0, 0);
        path.lineTo(size.width, 0);
        path.lineTo(size.width, size.height);
        break;
      case _Corner.bottomLeft:
        path.moveTo(0, 0);
        path.lineTo(0, size.height);
        path.lineTo(size.width, size.height);
        break;
      case _Corner.bottomRight:
        path.moveTo(size.width, 0);
        path.lineTo(size.width, size.height);
        path.lineTo(0, size.height);
        break;
    }
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant _CornerBracketPainter oldDelegate) => false;
}