import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

class FeedbackEntry {
  const FeedbackEntry({
    required this.id,
    required this.accurate,
    required this.createdAt,
    required this.photoPath,
    required this.cropPath,
    this.matchCardId,
    this.matchScore,
    this.matchDistance,
    this.matchFilename,
    this.matchConfident,
  });

  final String id;
  final bool accurate;
  final DateTime createdAt;
  final String photoPath;
  final String cropPath;
  final String? matchCardId;
  final double? matchScore;
  final int? matchDistance;
  final String? matchFilename;
  final bool? matchConfident;

  String? get matchAssetPath =>
      matchFilename == null ? null : 'test_assets/$matchFilename';

  Map<String, dynamic> toJson() => {
        'id': id,
        'accurate': accurate,
        'createdAt': createdAt.toIso8601String(),
        'photoFile': p.basename(photoPath),
        'cropFile': p.basename(cropPath),
        'matchCardId': matchCardId,
        'matchScore': matchScore,
        'matchDistance': matchDistance,
        'matchFilename': matchFilename,
        'matchConfident': matchConfident,
      };

  static FeedbackEntry fromJson(Map<String, dynamic> json, String dir) {
    return FeedbackEntry(
      id: json['id'] as String,
      accurate: json['accurate'] as bool? ?? false,
      createdAt: DateTime.tryParse(json['createdAt'] as String? ?? '') ??
          DateTime.fromMillisecondsSinceEpoch(0),
      photoPath: p.join(dir, json['photoFile'] as String),
      cropPath: p.join(dir, json['cropFile'] as String),
      matchCardId: json['matchCardId'] as String?,
      matchScore: (json['matchScore'] as num?)?.toDouble(),
      matchDistance: json['matchDistance'] as int?,
      matchFilename: json['matchFilename'] as String?,
      matchConfident: json['matchConfident'] as bool?,
    );
  }
}

/// Persists scan feedback under app documents (`feedback/`).
class FeedbackStore {
  FeedbackStore._();
  static final FeedbackStore instance = FeedbackStore._();

  Future<Directory> _dir() async {
    final root = await getApplicationDocumentsDirectory();
    final dir = Directory(p.join(root.path, 'feedback'));
    if (!await dir.exists()) {
      await dir.create(recursive: true);
    }
    return dir;
  }

  Future<File> _indexFile() async {
    final dir = await _dir();
    return File(p.join(dir.path, 'index.json'));
  }

  Future<List<FeedbackEntry>> list() async {
    final dir = await _dir();
    final index = await _indexFile();
    if (!await index.exists()) return const [];
    try {
      final raw = jsonDecode(await index.readAsString());
      if (raw is! List) return const [];
      final items = raw
          .whereType<Map>()
          .map((e) => FeedbackEntry.fromJson(Map<String, dynamic>.from(e), dir.path))
          .toList();
      items.sort((a, b) => b.createdAt.compareTo(a.createdAt));
      return items;
    } catch (_) {
      return const [];
    }
  }

  Future<FeedbackEntry> add({
    required bool accurate,
    required String photoSourcePath,
    required Uint8List cropBytes,
    String? matchCardId,
    double? matchScore,
    int? matchDistance,
    String? matchFilename,
    bool? matchConfident,
  }) async {
    final dir = await _dir();
    final id = DateTime.now().millisecondsSinceEpoch.toString();
    final photoPath = p.join(dir.path, '${id}_photo.jpg');
    final cropPath = p.join(dir.path, '${id}_crop.jpg');

    await File(photoSourcePath).copy(photoPath);
    await File(cropPath).writeAsBytes(cropBytes, flush: true);

    final entry = FeedbackEntry(
      id: id,
      accurate: accurate,
      createdAt: DateTime.now(),
      photoPath: photoPath,
      cropPath: cropPath,
      matchCardId: matchCardId,
      matchScore: matchScore,
      matchDistance: matchDistance,
      matchFilename: matchFilename,
      matchConfident: matchConfident,
    );

    final existing = await list();
    final next = [entry, ...existing];
    final index = await _indexFile();
    await index.writeAsString(
      const JsonEncoder.withIndent('  ').convert(
        next.map((e) => e.toJson()).toList(),
      ),
      flush: true,
    );
    return entry;
  }

  Future<void> delete(String id) async {
    final dir = await _dir();
    final existing = await list();
    final keep = existing.where((e) => e.id != id).toList();
    // Remove files for this entry.
    for (final suffix in ['_photo.jpg', '_crop.jpg']) {
      final f = File(p.join(dir.path, '$id$suffix'));
      try { await f.delete(); } catch (_) {}
    }
    final index = await _indexFile();
    if (keep.isEmpty) {
      try { await index.delete(); } catch (_) {}
      return;
    }
    await index.writeAsString(
      const JsonEncoder.withIndent('  ').convert(
        keep.map((e) => e.toJson()).toList(),
      ),
      flush: true,
    );
  }

  Future<void> clearAll() async {
    final dir = await _dir();
    if (await dir.exists()) {
      await dir.delete(recursive: true);
    }
  }
}
