import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:permission_handler/permission_handler.dart';
import '../services/tflite_service.dart';
import '../widgets/palm_bounding_box.dart';
import '../constants/mpob_colors.dart';
import '../services/app_strings.dart';
import '../theme/locale_controller.dart';

enum DetectionState { searching, locking, capturing, cooldown }

class LiveAnalysisScreen extends StatefulWidget {
  const LiveAnalysisScreen({super.key});

  @override
  State<LiveAnalysisScreen> createState() => _LiveAnalysisScreenState();
}

class _LiveAnalysisScreenState extends State<LiveAnalysisScreen> {
  CameraController? _controller;
  final TfliteService _tfliteService = TfliteService();

  bool _isInitialized = false;
  bool _isProcessing = false;
  List<DetectionResult>? _detections;
  
  // Detection Lock Logic
  DetectionState _state = DetectionState.searching;
  static const double _lockThreshold = 0.60;
  // Frame Throttle removed: Atomic Idle Lock active.
  
  final List<bool> _detectionHistory = List.filled(15, false, growable: true); // 15 frames buffer
  static const int _requiredHits = 5; // Target hits for momentum lock
  int _currentHits = 0;               // Track hits for the timer

  Timer? _lockTimer;
  Timer? _cooldownTimer;
  double _lockProgress = 0.0;
  bool _showFlash = false;

  // Raw CameraImage stream frames arrive in the sensor's native (landscape)
  // orientation, never pre-rotated for display — unlike CameraPreview's own
  // texture, which is rotated internally to appear upright. Detection boxes
  // decoded from the stream must be rotated by this same amount before
  // rendering, or they land in the wrong place/orientation relative to what's
  // shown on screen. Defaults to 90 (the standard rear-camera value on
  // Android) until the actual camera's sensorOrientation is known.
  int _sensorOrientation = 90;

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

  Future<void> _initializeCamera() async {
    final status = await Permission.camera.request();
    if (status.isDenied) return;

    final cameras = await availableCameras();
    if (cameras.isEmpty) return;

    _sensorOrientation = cameras[0].sensorOrientation;

    _controller = CameraController(
      cameras[0],
      ResolutionPreset.medium, // Upgraded resolution for YOLO26 performance
      enableAudio: false,
      imageFormatGroup: Platform.isAndroid ? ImageFormatGroup.yuv420 : ImageFormatGroup.bgra8888,
    );

    try {
      await _controller!.initialize();
      await _tfliteService.initModel();

      _controller!.startImageStream(_handleImageStream);

      if (mounted) {
        setState(() {
          _isInitialized = true;
        });
      }
    } catch (e) {
      debugPrint("Camera init error: $e");
    }
  }

  void _handleImageStream(CameraImage image) {
    // Atomic Idle Lock: Only process if the Persistent Isolate is truly idle
    if (_isProcessing || _tfliteService.isIsolateBusy || _state == DetectionState.capturing || _state == DetectionState.cooldown) {
      return; 
    }

    _processStreamFrame(image);
  }

  Future<void> _processStreamFrame(CameraImage image) async {
    setState(() => _isProcessing = true);
    try {
      final detections = _rotateDetections(await _tfliteService.runInferenceOnStream(image));

      bool currentFrameHasFruit = false;
      if (detections.isNotEmpty) {
        currentFrameHasFruit = detections.any((d) => d.confidence > _lockThreshold);
      }

      // Update Sliding Window Buffer
      _detectionHistory.removeAt(0);
      _detectionHistory.add(currentFrameHasFruit);

      _currentHits = _detectionHistory.where((h) => h).length;

      if (!mounted) return;

      setState(() {
        _detections = detections;
      });

      if (_state == DetectionState.searching) {
        if (_currentHits >= _requiredHits) {
          setState(() {
            _state = DetectionState.locking;
            _lockProgress = 0.0;
          });
          _startLockTimer();
        }
      }
      // Removed the old strict cancel logic.
      // _startLockTimer now safely handles momentum drain.
    } catch (e) {
      debugPrint("Stream processing error: $e");
    } finally {
      if (mounted) {
        setState(() => _isProcessing = false);
      }
    }
  }

  /// Rotates each detection's normalized box from raw sensor-space into
  /// display-space, matching the rotation CameraPreview's own texture
  /// undergoes internally. Confidence/class are untouched.
  List<DetectionResult> _rotateDetections(List<DetectionResult> detections) {
    final quarterTurns = (_sensorOrientation ~/ 90) % 4;
    if (quarterTurns == 0) return detections;
    return detections
        .map((d) => DetectionResult(
              className: d.className,
              classIndex: d.classIndex,
              confidence: d.confidence,
              normalizedBox: _rotateNormalizedRect(d.normalizedBox, quarterTurns),
            ))
        .toList();
  }

  /// Rotates an axis-aligned rect in normalized [0,1]x[0,1] space by
  /// [quarterTurns] * 90° clockwise.
  Rect _rotateNormalizedRect(Rect r, int quarterTurns) {
    switch (quarterTurns) {
      case 1: // 90° clockwise
        return Rect.fromLTRB(1 - r.bottom, r.left, 1 - r.top, r.right);
      case 2: // 180°
        return Rect.fromLTRB(1 - r.right, 1 - r.bottom, 1 - r.left, 1 - r.top);
      case 3: // 270° clockwise (90° counter-clockwise)
        return Rect.fromLTRB(r.top, 1 - r.right, r.bottom, 1 - r.left);
      default:
        return r;
    }
  }

  void _startLockTimer() {
    _lockTimer?.cancel();
    const duration = Duration(milliseconds: 100);
    int momentumTicks = 0;
    _lockTimer = Timer.periodic(duration, (timer) {
      if (!mounted) {
        timer.cancel();
        return;
      }
      
      // Momentum logic: add or subtract
      if (_currentHits >= _requiredHits) {
        momentumTicks++;
      } else {
        momentumTicks--;
      }
      
      if (momentumTicks < 0) momentumTicks = 0;
      
      setState(() {
        _lockProgress = (momentumTicks / 2.0).clamp(0.0, 1.0); // 2 ticks target
      });
      
      if (momentumTicks >= 2) {
        timer.cancel();
        if (_state == DetectionState.locking) {
          _triggerCapture();
        }
      } else if (momentumTicks <= 0 && _state == DetectionState.locking) {
        // Complete momentum loss -> Cancel lock
        timer.cancel();
        setState(() {
          _state = DetectionState.searching;
          _lockProgress = 0.0;
        });
      }
    });
  }

  Future<void> _triggerCapture() async {
    setState(() {
      _state = DetectionState.capturing;
      _lockProgress = 1.0;
      _showFlash = true;
    });
    
    // Quick 200ms white flash without blocking
    Future.delayed(const Duration(milliseconds: 200), () {
      if (mounted) setState(() => _showFlash = false);
    });
    
    await _captureAndAnalyze();
  }

  /// Live Inference is a quick verification tool, not a capture mode — the
  /// result is shown once and then discarded. Nothing is written to history;
  /// use Snap & Analyze if you want the result kept.
  Future<void> _captureAndAnalyze() async {
    if (_controller == null || !_controller!.value.isInitialized) return;

    // 1. Stop stream to avoid resource conflict
    await _controller!.stopImageStream();

    if (!mounted) return;

    try {
      // 2. Take high-res picture
      final XFile photo = await _controller!.takePicture();

      // 3. Run final inference on high-res
      final detections = await _tfliteService.runInference(photo.path);

      // 4. Discard the temp capture — nothing persists past this quick check.
      unawaited(File(photo.path).delete().catchError((_) => File(photo.path)));

      if (detections.isNotEmpty) {
        final best = detections.first;
        if (mounted) {
          await _showResultSheet(best.className, best.confidence);
          _startCooldown();
        }
      } else {
         if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text(AppStrings.t('live_no_detections_snackbar')))
          );
          _startCooldown();
        }
      }
    } catch (e) {
      debugPrint("Capture error: $e");
      if (mounted) _startCooldown();
    }
  }
  
  void _startCooldown() {
    if (!mounted) return;
    setState(() {
      _state = DetectionState.cooldown;
      _detections = null; // Clear boxes
    });
    
    // Clear detection history to ignore old hits
    _detectionHistory.fillRange(0, _detectionHistory.length, false);

    _cooldownTimer?.cancel();
    _cooldownTimer = Timer(const Duration(seconds: 3), () {
      if (!mounted) return;
      setState(() {
        _state = DetectionState.searching;
      });
      _controller?.startImageStream(_handleImageStream);
    });
  }

  Future<void> _showResultSheet(String ripenessClass, double confidence) async {
    final Color statusColor = getStatusColor(ripenessClass);
    final colorScheme = Theme.of(context).colorScheme;

    await showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      isDismissible: false,
      enableDrag: false,
      shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
      builder: (context) => Container(
        padding: const EdgeInsets.all(24),
        decoration: BoxDecoration(
          color: colorScheme.surfaceContainerHighest,
          borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
        ),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(Icons.check_circle, color: statusColor, size: 64),
            const SizedBox(height: 16),
            Text(AppStrings.className(ripenessClass), style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: colorScheme.onSurface)),
            Text(
              AppStrings.tp('confidence_label', {'percent': (confidence * 100).toStringAsFixed(1)}),
              style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.6)),
            ),
            const SizedBox(height: 24),
            if (ripenessClass == 'Empty_Bunch' || ripenessClass == 'Abnormal')
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(color: Colors.red.shade50, borderRadius: BorderRadius.circular(8)),
                child: Text(AppStrings.t('health_alert_abnormal'), style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold)),
              ),
            const SizedBox(height: 24),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: () => Navigator.pop(context),
                child: Text(AppStrings.t('done_button')),
              ),
            ),
          ],
        ),
      ),
    );
  }

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

  Widget _buildScaffold(BuildContext context) {
    if (!_isInitialized || _controller == null) {
      return const Scaffold(body: Center(child: CircularProgressIndicator()));
    }

    final isLockedVisual = _state == DetectionState.locking || _state == DetectionState.capturing;

    return Scaffold(
      backgroundColor: Colors.black,
      body: Stack(
        children: [
          // Camera Preview + Bounding Box Overlays. The overlay is passed as
          // CameraPreview's own `child` slot (not a separate sibling) so it's
          // measured against the exact same orientation-aware AspectRatio box
          // CameraPreview computes internally for the texture itself —
          // otherwise the overlay measures the full screen while the preview
          // is letterboxed/pillarboxed to the camera's real aspect ratio,
          // causing misaligned/stretched boxes.
          Center(
            child: CameraPreview(
              _controller!,
              child: _detections != null && _state != DetectionState.capturing && _state != DetectionState.cooldown
                  ? LayoutBuilder(
                      builder: (context, constraints) {
                        return Stack(
                          children: _detections!
                              .map((d) => PalmBoundingBox(
                                    normalizedRect: d.normalizedBox,
                                    label: d.className,
                                    confidence: d.confidence,
                                    constraints: constraints,
                                    isLocked: (_state == DetectionState.locking || _state == DetectionState.capturing) && d.confidence > _lockThreshold,
                                  ))
                              .toList(),
                        );
                      },
                    )
                  : null,
            ),
          ),

          // Top Info Bar
          Positioned(
            top: 40,
            left: 20,
            right: 20,
            child: Container(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              decoration: BoxDecoration(
                color: Colors.black54,
                borderRadius: BorderRadius.circular(20),
              ),
              child: Row(
                children: [
                  Icon(
                    _state == DetectionState.cooldown ? Icons.pause_circle_filled : 
                    isLockedVisual ? Icons.lock : Icons.center_focus_weak,
                    color: _state == DetectionState.cooldown ? Colors.blue : 
                           isLockedVisual ? Colors.green : Colors.yellow,
                  ),
                  const SizedBox(width: 8),
                  Text(
                    _state == DetectionState.cooldown ? AppStrings.t('live_state_cooldown') :
                    isLockedVisual ? AppStrings.t('live_state_locking') : AppStrings.t('live_state_searching'),
                    style: TextStyle(
                      color: _state == DetectionState.cooldown ? Colors.blue : 
                             isLockedVisual ? Colors.green : Colors.yellow,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const Spacer(),
                  IconButton(
                    icon: const Icon(Icons.close, color: Colors.white),
                    onPressed: () => Navigator.pop(context),
                  ),
                ],
              ),
            ),
          ),

          // Progress Overlay for Locking
          if (_state == DetectionState.locking)
            Positioned.fill(
              child: Center(
                child: SizedBox(
                  width: 120,
                  height: 120,
                  child: TweenAnimationBuilder<double>(
                    tween: Tween<double>(begin: 0.0, end: _lockProgress),
                    duration: const Duration(milliseconds: 100),
                    builder: (context, value, _) => CircularProgressIndicator(
                      value: value,
                      strokeWidth: 8,
                      color: Colors.greenAccent,
                      backgroundColor: Colors.white24,
                    ),
                  ),
                ),
              ),
            ),

          // White flash overlay
          Positioned.fill(
            child: IgnorePointer(
              child: AnimatedOpacity(
                opacity: _showFlash ? 1.0 : 0.0,
                duration: const Duration(milliseconds: 200),
                child: Container(color: Colors.white),
              ),
            ),
          ),
            
          if (_state == DetectionState.capturing && !_showFlash)
            Positioned.fill(
              child: Container(
                color: Colors.black45,
                child: const Center(
                  child: CircularProgressIndicator(color: Colors.white),
                ),
              ),
            ),
            
          if (_state == DetectionState.cooldown)
             Positioned.fill(
              child: Container(
                color: Colors.black45,
                child: Center(
                  child: Text(
                    AppStrings.t('live_resuming_scan'),
                    style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
                  ),
                ),
              ),
            ),

          // Bottom Hint
          if (_state == DetectionState.searching)
            Positioned(
              bottom: 40,
              left: 0,
              right: 0,
              child: Center(
                child: Text(
                  AppStrings.t('live_hold_steady_hint'),
                  style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500),
                ),
              ),
            ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _lockTimer?.cancel();
    _cooldownTimer?.cancel();
    _controller?.dispose();
    _tfliteService.dispose();
    super.dispose();
  }
}
