import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as p;
import '../models/palm_record.dart';
import '../models/palm_correction.dart';
import '../services/database_helper.dart';
import '../constants/mpob_colors.dart';
import '../theme/app_theme.dart';
import '../widgets/palm_bounding_box.dart';
import '../widgets/class_picker_sheet.dart';
import '../services/app_strings.dart';
import '../theme/locale_controller.dart';
import 'detection_editor_screen.dart';

/// Detection data parsed from the JSON-serialized `detections` list stored
/// on a [PalmRecord] (or a [PalmCorrection]'s corrected detections).
class DetectionData {
  final String className;
  final double confidence;
  final Rect normalizedBox;
  final bool isManual;

  const DetectionData({
    required this.className,
    required this.confidence,
    required this.normalizedBox,
    this.isManual = false,
  });

  factory DetectionData.fromMap(Map<String, dynamic> m) {
    return DetectionData(
      className: (m['className'] ?? m['class'] ?? 'Unknown').toString(),
      confidence: (m['confidence'] as num?)?.toDouble() ?? 0.0,
      normalizedBox: Rect.fromLTRB(
        (m['x1'] as num?)?.toDouble() ?? 0.0,
        (m['y1'] as num?)?.toDouble() ?? 0.0,
        (m['x2'] as num?)?.toDouble() ?? 1.0,
        (m['y2'] as num?)?.toDouble() ?? 1.0,
      ),
      isManual: m['isManual'] == true,
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'className': className,
      'classIndex': mpobClasses.indexOf(className),
      'confidence': confidence,
      'x1': normalizedBox.left,
      'y1': normalizedBox.top,
      'x2': normalizedBox.right,
      'y2': normalizedBox.bottom,
      'isManual': isManual,
    };
  }

  DetectionData copyWith({String? className, bool? isManual}) {
    return DetectionData(
      className: className ?? this.className,
      confidence: confidence,
      normalizedBox: normalizedBox,
      isManual: isManual ?? this.isManual,
    );
  }
}

/// Full-screen record detail view with prev/next navigation between the
/// frames of the same batch (or a single-frame list for standalone records).
class RecordDetailScreen extends StatefulWidget {
  final List<PalmRecord> frames;
  final int initialIndex;

  const RecordDetailScreen({super.key, required this.frames, required this.initialIndex});

  @override
  State<RecordDetailScreen> createState() => _RecordDetailScreenState();
}

class _RecordDetailScreenState extends State<RecordDetailScreen> {
  late final PageController _pageController;
  late int _index;

  @override
  void initState() {
    super.initState();
    _index = widget.initialIndex;
    _pageController = PageController(initialPage: widget.initialIndex);
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  void _goTo(int index) {
    _pageController.animateToPage(
      index,
      duration: const Duration(milliseconds: 250),
      curve: Curves.easeOut,
    );
  }

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

  Widget _buildScaffold(BuildContext context) {
    final isFirst = _index == 0;
    final isLast = _index == widget.frames.length - 1;

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.frames.length > 1
            ? AppStrings.tp('frame_of_title', {'current': '${_index + 1}', 'total': '${widget.frames.length}'})
            : AppStrings.t('record_details_title')),
        actions: widget.frames.length > 1
            ? [
                IconButton(
                  icon: const Icon(Icons.chevron_left),
                  onPressed: isFirst ? null : () => _goTo(_index - 1),
                ),
                IconButton(
                  icon: const Icon(Icons.chevron_right),
                  onPressed: isLast ? null : () => _goTo(_index + 1),
                ),
              ]
            : null,
      ),
      body: PageView.builder(
        controller: _pageController,
        itemCount: widget.frames.length,
        onPageChanged: (i) => setState(() => _index = i),
        itemBuilder: (context, i) => _RecordDetailBody(record: widget.frames[i]),
      ),
    );
  }
}

enum _ViewMode { ai, corrected }

class _RecordDetailBody extends StatefulWidget {
  final PalmRecord record;
  const _RecordDetailBody({required this.record});

  @override
  State<_RecordDetailBody> createState() => _RecordDetailBodyState();
}

class _RecordDetailBodyState extends State<_RecordDetailBody> {
  final DatabaseHelper _dbHelper = DatabaseHelper();

  _ViewMode _viewMode = _ViewMode.ai;
  late List<DetectionData> _aiDetections;
  late List<DetectionData> _correctionDraft;
  DateTime? _lastCorrectedAt;
  bool _isLoading = true;
  bool _isSaving = false;

  bool get _hasCorrection => _lastCorrectedAt != null;

  @override
  void initState() {
    super.initState();
    _aiDetections = widget.record.detections.map((d) => DetectionData.fromMap(d)).toList();
    _loadExistingCorrection();
  }

  Future<void> _loadExistingCorrection() async {
    final recordId = widget.record.id;
    if (recordId == null) {
      setState(() {
        _correctionDraft = List.of(_aiDetections);
        _isLoading = false;
      });
      return;
    }

    final existing = await _dbHelper.getLatestCorrection(recordId);
    if (!mounted) return;
    setState(() {
      if (existing != null) {
        _correctionDraft = existing.correctedDetections.map((d) => DetectionData.fromMap(d)).toList();
        _lastCorrectedAt = existing.timestamp;
        _viewMode = _ViewMode.corrected;
      } else {
        _correctionDraft = List.of(_aiDetections);
      }
      _isLoading = false;
    });
  }

  Future<void> _editDetection(int index) async {
    final result = await showClassPickerSheet(
      context,
      currentClass: _correctionDraft[index].className,
      showRemoveOption: true,
    );
    if (result == null) return;

    if (result.remove) {
      setState(() => _correctionDraft.removeAt(index));
      await _persistCorrection();
    } else if (result.className != null) {
      setState(() {
        _correctionDraft[index] = _correctionDraft[index].copyWith(className: result.className);
      });
      await _persistCorrection();
    }
  }

  Future<void> _handleBoxTap(int index) async {
    if (_viewMode != _ViewMode.corrected) {
      setState(() => _viewMode = _ViewMode.corrected);
    }
    await _editDetection(index);
  }

  /// Opens the full-screen editor for placing boxes the AI missed entirely.
  /// Always available — even a photo with zero AI detections needs a way
  /// into correction mode.
  Future<void> _openDetectionEditor() async {
    if (_viewMode != _ViewMode.corrected) {
      setState(() => _viewMode = _ViewMode.corrected);
    }
    final added = await Navigator.push<List<DetectionData>>(
      context,
      MaterialPageRoute(
        builder: (context) => DetectionEditorScreen(
          imagePath: widget.record.imagePath,
          existingDetections: _correctionDraft,
        ),
      ),
    );
    if (added != null && added.isNotEmpty) {
      setState(() => _correctionDraft.addAll(added));
      await _persistCorrection();
    }
  }

  /// Writes the current [_correctionDraft] as a new correction row. Called
  /// automatically after every box edit and after a reset — there is no
  /// separate manual "save" step to forget.
  Future<void> _persistCorrection() async {
    final recordId = widget.record.id;
    if (recordId == null || _correctionDraft.isEmpty) return;

    setState(() => _isSaving = true);
    final correction = PalmCorrection(
      originalRecordId: recordId,
      correctedClass: _correctionDraft.first.className,
      correctedDetections: _correctionDraft.map((d) => d.toMap()).toList(),
      timestamp: DateTime.now(),
    );
    await _dbHelper.insertCorrection(correction);

    if (!mounted) return;
    setState(() {
      _lastCorrectedAt = correction.timestamp;
      _isSaving = false;
    });
  }

  /// Wipes any saved correction for this photo entirely — back to a clean
  /// "no correction yet" state, so the correction tab hides again.
  Future<void> _resetCorrection() async {
    final recordId = widget.record.id;
    if (recordId != null) {
      await _dbHelper.deleteCorrectionsForRecord(recordId);
    }
    if (!mounted) return;
    setState(() {
      _correctionDraft = List.of(_aiDetections);
      _lastCorrectedAt = null;
      _viewMode = _ViewMode.ai;
    });
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(AppStrings.t('correction_reset_snackbar'))),
    );
  }

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

  Widget _buildBody(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final accents = AppTheme.chartAccents;

    if (_isLoading) {
      return const Center(child: CircularProgressIndicator());
    }

    final record = widget.record;
    final fileName = p.basename(record.imagePath);
    final activeDetections = _viewMode == _ViewMode.ai ? _aiDetections : _correctionDraft;
    final primaryGradeKey = activeDetections.isEmpty ? record.ripenessClass : activeDetections.first.className;
    final tally = <String, int>{};
    for (final d in activeDetections) {
      tally[d.className] = (tally[d.className] ?? 0) + 1;
    }

    return SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (_hasCorrection) ...[
            SegmentedButton<_ViewMode>(
              segments: [
                ButtonSegment(value: _ViewMode.ai, label: Text(AppStrings.t('ai_result_label')), icon: const Icon(Icons.smart_toy_outlined)),
                ButtonSegment(value: _ViewMode.corrected, label: Text(AppStrings.t('your_correction_label')), icon: const Icon(Icons.edit_outlined)),
              ],
              selected: {_viewMode},
              showSelectedIcon: false,
              style: SegmentedButton.styleFrom(
                selectedBackgroundColor: colorScheme.primary,
                selectedForegroundColor: colorScheme.onPrimary,
              ),
              onSelectionChanged: (selection) => setState(() => _viewMode = selection.first),
            ),
            const SizedBox(height: 8),
          ],
          Row(
            children: [
              Expanded(
                child: Text(
                  _viewMode == _ViewMode.ai
                      ? AppStrings.t('tap_to_correct_hint')
                      : (_isSaving
                          ? AppStrings.t('saving_label')
                          : AppStrings.tp('last_corrected_label', {'timestamp': _lastCorrectedAt.toString().split('.')[0]})),
                  style: TextStyle(fontSize: 12, color: colorScheme.onSurface.withValues(alpha: 0.6)),
                ),
              ),
              if (_viewMode == _ViewMode.corrected && _hasCorrection)
                TextButton.icon(
                  onPressed: _isSaving ? null : _resetCorrection,
                  icon: const Icon(Icons.restart_alt, size: 18),
                  label: Text(AppStrings.t('reset_button')),
                ),
            ],
          ),
          const SizedBox(height: 8),
          Align(
            alignment: Alignment.centerLeft,
            child: OutlinedButton.icon(
              onPressed: _isSaving ? null : _openDetectionEditor,
              icon: const Icon(Icons.add_box_outlined, size: 18),
              label: Text(AppStrings.t('add_missed_bunch_button')),
            ),
          ),
          const SizedBox(height: 16),

          // Image with Bounding Boxes
          AspectRatio(
            aspectRatio: 1, // Assuming squared input 640x640
            child: Container(
              decoration: BoxDecoration(
                color: colorScheme.surfaceContainerHighest,
                borderRadius: BorderRadius.circular(12),
              ),
              child: record.imagePath.isNotEmpty && File(record.imagePath).existsSync()
                  ? ClipRRect(
                      borderRadius: BorderRadius.circular(12),
                      child: Stack(
                        children: [
                          Positioned.fill(
                            child: Image.file(File(record.imagePath), fit: BoxFit.contain),
                          ),
                          Positioned.fill(
                            child: LayoutBuilder(
                              builder: (context, constraints) {
                                final boxes = activeDetections.map<Widget>((d) {
                                  return PalmBoundingBox(
                                    normalizedRect: d.normalizedBox,
                                    label: d.className,
                                    confidence: d.confidence,
                                    constraints: constraints,
                                    isManual: d.isManual,
                                  );
                                }).toList();

                                // Every box is tappable, in both views — tapping
                                // one always jumps into (or stays in) correction mode.
                                final tapTargets = activeDetections.asMap().entries.map<Widget>((entry) {
                                  final i = entry.key;
                                  final rect = entry.value.normalizedBox;
                                  return Positioned(
                                    left: rect.left * constraints.maxWidth,
                                    top: rect.top * constraints.maxHeight,
                                    width: rect.width * constraints.maxWidth,
                                    height: rect.height * constraints.maxHeight,
                                    child: GestureDetector(
                                      behavior: HitTestBehavior.opaque,
                                      onTap: () => _handleBoxTap(i),
                                    ),
                                  );
                                }).toList();

                                return Stack(children: [...boxes, ...tapTargets]);
                              },
                            ),
                          ),
                        ],
                      ),
                    )
                  : Center(
                      child: Icon(Icons.image, size: 64, color: colorScheme.onSurface.withValues(alpha: 0.4)),
                    ),
            ),
          ),

          const SizedBox(height: 24),
          _buildDetailItem(AppStrings.t('label_file_name'), fileName, accents[4], colorScheme),
          _buildDetailItem(AppStrings.t('label_primary_grade'), AppStrings.className(primaryGradeKey), getStatusColor(primaryGradeKey), colorScheme),
          _buildDetailItem(AppStrings.t('kpi_total_detections'), activeDetections.length.toString(), colorScheme.onSurface, colorScheme),
          _buildDetailItem(AppStrings.t('label_inference_time'), record.inferenceMs == null ? AppStrings.t('na_label') : "${record.inferenceMs} ms", accents[3], colorScheme),
          _buildDetailItem(AppStrings.t('label_timestamp'), record.timestamp.toString().split('.')[0], colorScheme.onSurface, colorScheme),

          if (tally.isNotEmpty) ...[
            const Divider(height: 32),
            Text(AppStrings.t('frame_summary_title'), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: colorScheme.onSurface)),
            const SizedBox(height: 12),
            Wrap(
              spacing: 16,
              runSpacing: 8,
              children: tally.entries.map((e) {
                return Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Container(width: 12, height: 12, decoration: BoxDecoration(color: getMPOBColor(e.key), shape: BoxShape.circle)),
                    const SizedBox(width: 6),
                    Text("${AppStrings.className(e.key)}: ${e.value}", style: TextStyle(fontSize: 13, color: colorScheme.onSurface)),
                  ],
                );
              }).toList(),
            ),
          ],

          const Divider(height: 32),
          Text(
            _viewMode == _ViewMode.ai ? AppStrings.t('analytical_breakdown_title') : AppStrings.t('corrected_breakdown_title'),
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: colorScheme.onSurface),
          ),
          const SizedBox(height: 12),

          ...activeDetections.map((d) => Padding(
            padding: const EdgeInsets.only(bottom: 8),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Row(
                  children: [
                    Container(
                      width: 12,
                      height: 12,
                      decoration: BoxDecoration(
                        color: getStatusColor(d.className),
                        shape: BoxShape.circle,
                      ),
                    ),
                    const SizedBox(width: 8),
                    Text(AppStrings.className(d.className), style: TextStyle(fontWeight: FontWeight.w500, color: colorScheme.onSurface)),
                  ],
                ),
                Text("${(d.confidence * 100).toStringAsFixed(1)}%", style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.6))),
              ],
            ),
          )),

          const SizedBox(height: 24),
          Text(
            AppStrings.t('industrial_summary_text'),
            style: TextStyle(fontStyle: FontStyle.italic, color: colorScheme.onSurface.withValues(alpha: 0.6)),
          ),
          const SizedBox(height: 24),
        ],
      ),
    );
  }

  Widget _buildDetailItem(String label, String value, Color valueColor, ColorScheme colorScheme) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text("$label: ", style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16, color: colorScheme.onSurface)),
          Expanded(
            child: Text(value, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: valueColor)),
          ),
        ],
      ),
    );
  }
}
