import 'dart:io';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart';

import '../services/card_cropper.dart';
import '../services/card_detector.dart';
import '../services/feedback_store.dart';
import '../services/hash_library_asset.dart';
import '../services/phash_matcher.dart';
import '../services/rgb_phash.dart';
import '../widgets/guide_frame.dart';

/// Post-capture: detect / lock border on still image, then crop for preview.
class PreviewScreen extends StatefulWidget {
  const PreviewScreen({
    super.key,
    required this.imagePath,
    this.captureViewport,
  });

  final String imagePath;

  /// Camera preview viewport used to map the shooting guide into image pixels.
  /// Null for gallery picks — guide is applied in image space directly.
  final Size? captureViewport;

  @override
  State<PreviewScreen> createState() => _PreviewScreenState();
}

class _PreviewScreenState extends State<PreviewScreen> {
  final _detector = CardDetector();
  final _phash = RgbPhash();
  final _matcher = PhashMatcher();

  bool _loading = true;
  String? _error;
  ui.Image? _sourceImage;
  CardBox? _locked;
  CropResult? _crop;
  String? _label;
  double? _confidence;
  List<PhashMatch> _matches = const [];
  String? _matchError;
  String? _guidePath;
  bool? _feedbackAccurate;
  bool _feedbackBusy = false;

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

  @override
  void dispose() {
    _sourceImage?.dispose();
    _detector.dispose();
    super.dispose();
  }

  Future<void> _run() async {
    try {
      // Decode with Flutter (handles gallery HEIC/EXIF well, keeps sharpness).
      final raw = await File(widget.imagePath).readAsBytes();
      final displayCodec = await ui.instantiateImageCodec(raw);
      final displayFrame = await displayCodec.getNextFrame();
      final fullImage = displayFrame.image;

      final rgba = await fullImage.toByteData(
        format: ui.ImageByteFormat.rawRgba,
      );
      if (rgba == null) {
        fullImage.dispose();
        throw StateError('无法读取像素');
      }

      final fullRaster = img.Image.fromBytes(
        width: fullImage.width,
        height: fullImage.height,
        bytes: rgba.buffer,
        bytesOffset: rgba.offsetInBytes,
        numChannels: 4,
        order: img.ChannelOrder.rgba,
      );
      fullImage.dispose();

      // Initial crop to the shooting guide — exclude everything outside the frame.
      final imageSize = Size(
        fullRaster.width.toDouble(),
        fullRaster.height.toDouble(),
      );
      final guide = widget.captureViewport != null
          ? GuideFrame.inCoverImage(
              viewport: widget.captureViewport!,
              imageSize: imageSize,
            )
          : GuideFrame.inImage(imageSize);

      final gx = guide.left.round().clamp(0, fullRaster.width - 1);
      final gy = guide.top.round().clamp(0, fullRaster.height - 1);
      final gw = guide.width.round().clamp(1, fullRaster.width - gx);
      final gh = guide.height.round().clamp(1, fullRaster.height - gy);
      final raster = img.copyCrop(
        fullRaster,
        x: gx,
        y: gy,
        width: gw,
        height: gh,
      );

      final dir = await getTemporaryDirectory();
      final guidePath =
          '${dir.path}/card_guide_${DateTime.now().millisecondsSinceEpoch}.jpg';
      await File(guidePath).writeAsBytes(
        img.encodeJpg(raster, quality: 100),
        flush: true,
      );

      // Build a ui.Image for the guide-cropped preview.
      final guideBytes = img.encodeJpg(raster, quality: 100);
      final guideCodec = await ui.instantiateImageCodec(guideBytes);
      final guideFrame = await guideCodec.getNextFrame();
      final displayImage = guideFrame.image;

      final boxes = await _detector.detectFile(guidePath);
      final guideSize = Size(
        displayImage.width.toDouble(),
        displayImage.height.toDouble(),
      );
      var best = _detector.pickBest(boxes, imageSize: guideSize);
      if (best == null || boxes.isEmpty) {
        displayImage.dispose();
        if (!mounted) return;
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('未识别到卡牌边框，请重拍')),
        );
        Navigator.of(context).pop();
        return;
      }
      best = _detector.refineCorners(raster, best);

      final crop = await CardCropper.cropAndCorrect(
        imagePath: guidePath,
        box: best,
      );

      String? matchError;
      var matches = <PhashMatch>[];
      try {
        final phashValue = _phash.fromJpegBytes(crop.bytes);
        final library = await loadHashLibraryAsset();
        matches = _matcher.topN(phashValue, library, n: 3);
      } catch (e) {
        matchError = 'pHash 比对失败: $e';
      }

      if (!mounted) {
        displayImage.dispose();
        return;
      }
      setState(() {
        _sourceImage = displayImage;
        _locked = best;
        _crop = crop;
        _label = best?.label;
        _confidence = best?.confidence;
        _matches = matches;
        _matchError = matchError;
        _guidePath = guidePath;
        _loading = false;
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _error = '处理失败: $e';
        _loading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('边框锁定预览'),
        backgroundColor: Colors.transparent,
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.fromLTRB(20, 8, 20, 24),
          child: _loading
              ? const Center(
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      CircularProgressIndicator(),
                      SizedBox(height: 16),
                      Text('正在锁定卡牌边框…'),
                    ],
                  ),
                )
              : _error != null
                  ? Center(child: Text(_error!, textAlign: TextAlign.center))
                  : _buildContent(context),
        ),
      ),
    );
  }

  Widget _buildContent(BuildContext context) {
    final meta = _locked != null
        ? (_locked!.corners != null
            ? '已锁定倾斜边框${_label != null ? ' · $_label' : ''}'
                '${_confidence != null ? ' · ${(_confidence! * 100).toStringAsFixed(0)}%' : ''}'
            : '已锁定边框${_label != null ? ' · $_label' : ''}'
                '${_confidence != null ? ' · ${(_confidence! * 100).toStringAsFixed(0)}%' : ''}')
        : '未检出边框 · 已用引导框裁剪';

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Text(
          meta,
          style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                color: Colors.white70,
              ),
        ),
        const SizedBox(height: 8),
        Expanded(
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                SizedBox(
                  height: 220,
                  child: _LockedPhotoView(
                    image: _sourceImage!,
                    locked: _locked,
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  '裁剪结果',
                  style: Theme.of(context).textTheme.titleSmall,
                ),
                const SizedBox(height: 6),
                SizedBox(
                  height: 180,
                  child: Center(
                    child: DecoratedBox(
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(12),
                        boxShadow: const [
                          BoxShadow(
                            color: Color(0x66000000),
                            blurRadius: 24,
                            offset: Offset(0, 12),
                          ),
                        ],
                      ),
                      child: ClipRRect(
                        borderRadius: BorderRadius.circular(12),
                        child: Image.memory(
                          _crop!.bytes,
                          fit: BoxFit.contain,
                          filterQuality: FilterQuality.high,
                        ),
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 8),
                _buildMatchSection(context),
                const SizedBox(height: 10),
                _buildFeedbackRow(context),
                const SizedBox(height: 10),
                FilledButton.icon(
                  onPressed: () => Navigator.of(context).pop(),
                  icon: const Icon(Icons.camera_alt_outlined),
                  label: const Text('重新拍摄'),
                  style: FilledButton.styleFrom(
                    minimumSize: const Size.fromHeight(48),
                  ),
                ),
                const SizedBox(height: 8),
              ],
            ),
          ),
        ),
      ],
    );
  }

  Widget _buildFeedbackRow(BuildContext context) {
    final submitted = _feedbackAccurate != null;
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Text(
          submitted
              ? (_feedbackAccurate! ? '已记录：准确' : '已记录：不准确')
              : '匹配结果反馈',
          style: Theme.of(context).textTheme.titleSmall,
        ),
        const SizedBox(height: 8),
        Row(
          children: [
            Expanded(
              child: OutlinedButton.icon(
                onPressed: submitted || _feedbackBusy
                    ? null
                    : () => _submitFeedback(accurate: true),
                icon: const Icon(Icons.thumb_up_alt_outlined, size: 18),
                label: const Text('准确'),
                style: OutlinedButton.styleFrom(
                  foregroundColor: const Color(0xFF22C55E),
                  side: BorderSide(
                    color: _feedbackAccurate == true
                        ? const Color(0xFF22C55E)
                        : Colors.white24,
                  ),
                  minimumSize: const Size.fromHeight(44),
                ),
              ),
            ),
            const SizedBox(width: 10),
            Expanded(
              child: OutlinedButton.icon(
                onPressed: submitted || _feedbackBusy
                    ? null
                    : () => _submitFeedback(accurate: false),
                icon: const Icon(Icons.thumb_down_alt_outlined, size: 18),
                label: const Text('不准确'),
                style: OutlinedButton.styleFrom(
                  foregroundColor: const Color(0xFFEF4444),
                  side: BorderSide(
                    color: _feedbackAccurate == false
                        ? const Color(0xFFEF4444)
                        : Colors.white24,
                  ),
                  minimumSize: const Size.fromHeight(44),
                ),
              ),
            ),
          ],
        ),
      ],
    );
  }

  Future<void> _submitFeedback({required bool accurate}) async {
    final crop = _crop;
    final guidePath = _guidePath;
    if (crop == null || guidePath == null) return;
    if (_feedbackBusy || _feedbackAccurate != null) return;

    setState(() => _feedbackBusy = true);
    try {
      final top = _matches.isEmpty ? null : _matches.first;
      await FeedbackStore.instance.add(
        accurate: accurate,
        photoSourcePath: guidePath,
        cropBytes: crop.bytes,
        matchCardId: top?.cardId,
        matchScore: top?.score,
        matchDistance: top?.distance,
        matchFilename: top?.filename,
        matchConfident: top?.confident,
      );
      if (!mounted) return;
      setState(() {
        _feedbackAccurate = accurate;
        _feedbackBusy = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(accurate ? '已记录为准确' : '已记录为不准确'),
          duration: const Duration(seconds: 1),
        ),
      );
    } catch (e) {
      if (!mounted) return;
      setState(() => _feedbackBusy = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('反馈保存失败: $e')),
      );
    }
  }

  Widget _buildMatchSection(BuildContext context) {
    final theme = Theme.of(context).textTheme;
    if (_matchError != null) {
      return Text(
        _matchError!,
        style: theme.bodySmall?.copyWith(color: Colors.orangeAccent),
      );
    }
    if (_matches.isEmpty) {
      return Text(
        '本地图库暂无匹配结果',
        style: theme.bodySmall?.copyWith(color: Colors.white54),
      );
    }

    final top = _matches.first;
    final status = top.confident ? 'confident' : 'low';

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Text(
          '相似结果  ·  top1 ${top.cardId}  ${top.score} ($status)',
          style: theme.titleSmall,
        ),
        const SizedBox(height: 8),
        SizedBox(
          height: 148,
          child: ListView.separated(
            scrollDirection: Axis.horizontal,
            itemCount: _matches.length,
            separatorBuilder: (_, __) => const SizedBox(width: 10),
            itemBuilder: (context, index) {
              final m = _matches[index];
              return _MatchThumb(rank: index + 1, match: m);
            },
          ),
        ),
      ],
    );
  }
}

class _MatchThumb extends StatelessWidget {
  const _MatchThumb({required this.rank, required this.match});

  final int rank;
  final PhashMatch match;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context).textTheme;
    final asset = match.assetPath;
    return SizedBox(
      width: 96,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: ClipRRect(
              borderRadius: BorderRadius.circular(8),
              child: ColoredBox(
                color: Colors.black26,
                child: asset == null
                    ? const Center(
                        child: Icon(Icons.image_not_supported_outlined),
                      )
                    : Image.asset(
                        asset,
                        fit: BoxFit.cover,
                        errorBuilder: (_, __, ___) => const Center(
                          child: Icon(Icons.broken_image_outlined),
                        ),
                      ),
              ),
            ),
          ),
          const SizedBox(height: 4),
          Text(
            '#$rank ${match.score}',
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: theme.labelSmall?.copyWith(color: Colors.white70),
          ),
          Text(
            match.cardId,
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: theme.labelSmall?.copyWith(color: Colors.white54),
          ),
        ],
      ),
    );
  }
}

class _LockedPhotoView extends StatelessWidget {
  const _LockedPhotoView({
    required this.image,
    required this.locked,
  });

  final ui.Image image;
  final CardBox? locked;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final view = Size(constraints.maxWidth, constraints.maxHeight);
        final img = Size(image.width.toDouble(), image.height.toDouble());
        final fitted = _contain(img, view);

        return ClipRRect(
          borderRadius: BorderRadius.circular(12),
          child: ColoredBox(
            color: Colors.black26,
            child: Stack(
              children: [
                Positioned.fromRect(
                  rect: fitted,
                  child: RawImage(
                    image: image,
                    fit: BoxFit.fill,
                    filterQuality: FilterQuality.high,
                  ),
                ),
                if (locked != null)
                  Positioned.fill(
                    child: CustomPaint(
                      painter: _LockPainter(
                        box: locked!,
                        imageSize: img,
                        fitted: fitted,
                      ),
                    ),
                  ),
              ],
            ),
          ),
        );
      },
    );
  }

  static Rect _contain(Size image, Size view) {
    final scale = (view.width / image.width < view.height / image.height)
        ? view.width / image.width
        : view.height / image.height;
    final w = image.width * scale;
    final h = image.height * scale;
    return Rect.fromLTWH(
      (view.width - w) / 2,
      (view.height - h) / 2,
      w,
      h,
    );
  }
}

class _LockPainter extends CustomPainter {
  _LockPainter({
    required this.box,
    required this.imageSize,
    required this.fitted,
  });

  final CardBox box;
  final Size imageSize;
  final Rect fitted;

  Offset _map(Offset p) => Offset(
        fitted.left + p.dx / imageSize.width * fitted.width,
        fitted.top + p.dy / imageSize.height * fitted.height,
      );

  @override
  void paint(Canvas canvas, Size size) {
    final q = box.quad.map(_map).toList(growable: false);
    final path = Path()
      ..moveTo(q[0].dx, q[0].dy)
      ..lineTo(q[1].dx, q[1].dy)
      ..lineTo(q[2].dx, q[2].dy)
      ..lineTo(q[3].dx, q[3].dy)
      ..close();

    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 3
      ..color = const Color(0xFF22C55E);
    canvas.drawPath(path, paint);

    final corner = Paint()
      ..color = const Color(0xFF22C55E)
      ..strokeWidth = 4
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;
    const len = 22.0;
    for (var i = 0; i < 4; i++) {
      final p = q[i];
      final prev = q[(i + 3) % 4];
      final next = q[(i + 1) % 4];
      final toPrev = prev - p;
      final toNext = next - p;
      final dPrev = toPrev.distance;
      final dNext = toNext.distance;
      if (dPrev < 1 || dNext < 1) continue;
      final a = p + toPrev * (len / dPrev);
      final b = p + toNext * (len / dNext);
      canvas.drawLine(p, a, corner);
      canvas.drawLine(p, b, corner);
    }
  }

  @override
  bool shouldRepaint(covariant _LockPainter old) {
    return old.box != box || old.fitted != fitted || old.imageSize != imageSize;
  }
}
