import 'dart:io';
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:flutter/foundation.dart';
import 'gemini_service.dart';

/// 画像ハッシュベースのAI解析キャッシュサービス
///
/// 目的:
/// - 同じ日本酒を複数回撮影してもAPI呼び出しは1回のみ
/// - Gemini API制限（1日20回）の節約
///
/// 仕組み:
/// 1. 画像のSHA-256ハッシュを計算
/// 2. Hiveに{hash: 解析結果}を保存
/// 3. 同じハッシュの画像は即座にキャッシュから返す
class AnalysisCacheService {
  static const String _cacheBoxName = 'analysis_cache_v1';
  static const String _brandIndexBoxName = 'brand_index_v1'; // 銘柄名→ハッシュのインデックス
  static Box<String>? _box;
  static Box<String>? _brandIndexBox;

  /// キャッシュボックスの初期化
  static Future<void> init() async {
    if (_box != null && _brandIndexBox != null) return; // 既に初期化済み

    try {
      _box = await Hive.openBox<String>(_cacheBoxName);
      _brandIndexBox = await Hive.openBox<String>(_brandIndexBoxName);
      debugPrint('✅ Analysis Cache initialized (${_box!.length} entries, ${_brandIndexBox!.length} brands)');
    } catch (e) {
      debugPrint('⚠️ Failed to open cache box: $e');
    }
  }

  /// 画像のSHA-256ハッシュを計算
  ///
  /// 同じ写真は同じハッシュになる（ビット完全一致）
  /// 例: "abc123..." (64文字の16進数文字列)
  static Future<String> computeImageHash(String imagePath) async {
    try {
      final bytes = await File(imagePath).readAsBytes();
      final digest = sha256.convert(bytes);
      return digest.toString();
    } catch (e) {
      debugPrint('⚠️ Hash computation failed: $e');
      // ハッシュ計算失敗時はパスをそのまま使用（キャッシュなし）
      return imagePath;
    }
  }

  /// 複数画像の結合ハッシュを計算
  ///
  /// 複数枚の写真を組み合わせた場合、順序も考慮してハッシュ化
  /// 例: ["image1.jpg", "image2.jpg"] → "combined_hash_abc123..."
  static Future<String> computeCombinedHash(List<String> imagePaths) async {
    if (imagePaths.isEmpty) return '';
    if (imagePaths.length == 1) return computeImageHash(imagePaths.first);

    // 各画像のハッシュを連結してから再ハッシュ
    final hashes = await Future.wait(
      imagePaths.map((path) => computeImageHash(path)),
    );
    final combined = hashes.join('_');
    return sha256.convert(utf8.encode(combined)).toString();
  }

  /// キャッシュから取得
  ///
  /// 戻り値: キャッシュがあれば解析結果、なければnull
  static Future<SakeAnalysisResult?> getCached(String imageHash) async {
    await init();
    if (_box == null) return null;

    try {
      final jsonString = _box!.get(imageHash);
      if (jsonString == null) {
        debugPrint('🔍 Cache MISS: $imageHash');
        return null;
      }

      final jsonMap = jsonDecode(jsonString) as Map<String, dynamic>;
      debugPrint('✅ Cache HIT: ${jsonMap['name'] ?? 'Unknown'} ($imageHash)');
      return SakeAnalysisResult.fromJson(jsonMap);
    } catch (e) {
      debugPrint('⚠️ Cache read error: $e');
      return null;
    }
  }

  /// キャッシュに保存
  ///
  /// 解析結果をJSON化してHiveに永続化
  static Future<void> saveCache(String imageHash, SakeAnalysisResult result) async {
    await init();
    if (_box == null) return;

    try {
      final jsonMap = result.toJson();
      final jsonString = jsonEncode(jsonMap);
      await _box!.put(imageHash, jsonString);
      debugPrint('💾 Cache SAVED: ${result.name ?? 'Unknown'} ($imageHash)');
    } catch (e) {
      debugPrint('⚠️ Cache save error: $e');
    }
  }

  /// キャッシュをクリア（デバッグ用）
  static Future<void> clearAll() async {
    await init();
    if (_box == null) return;

    final count = _box!.length;
    await _box!.clear();
    debugPrint('🗑️ Cache cleared ($count entries deleted)');
  }

  /// キャッシュサイズを取得（統計用）
  static Future<int> getCacheSize() async {
    await init();
    return _box?.length ?? 0;
  }

  /// キャッシュの有効期限チェック（将来実装）
  ///
  /// 現在は永続キャッシュだが、将来的に有効期限を設定する場合:
  /// - 30日経過したキャッシュは削除
  /// - 日本酒の仕様変更（リニューアル）に対応
  static Future<void> cleanupExpired() async {
    // TODO: キャッシュにタイムスタンプを追加し、30日以上古いエントリを削除する
  }

  // ============================================
  // 銘柄名ベースキャッシュ機能（v1.0.15追加）
  // ============================================

  /// 銘柄名でキャッシュを検索
  ///
  /// 異なる写真でも同じ銘柄なら、以前の解析結果を返す
  /// これによりチャート（tasteStats）の一貫性を保証
  static Future<SakeAnalysisResult?> getCachedByBrand(String? brandName) async {
    if (brandName == null || brandName.isEmpty) return null;
    await init();
    if (_brandIndexBox == null) return null;

    try {
      final normalized = _normalizeBrandName(brandName);
      final imageHash = _brandIndexBox!.get(normalized);
      if (imageHash == null) {
        debugPrint('🔍 Brand Index MISS: $brandName');
        return null;
      }

      debugPrint('✅ Brand Index HIT: $brandName → $imageHash');
      return getCached(imageHash);
    } catch (e) {
      debugPrint('⚠️ Brand index lookup error: $e');
      return null;
    }
  }

  /// 銘柄名をインデックスに登録
  ///
  /// 解析結果のキャッシュ保存後に呼び出す
  /// 既にインデックスがある場合は上書きしない（最初の結果を優先）
  static Future<void> registerBrandIndex(String? brandName, String imageHash) async {
    if (brandName == null || brandName.isEmpty) return;
    await init();
    if (_brandIndexBox == null) return;

    try {
      final normalized = _normalizeBrandName(brandName);

      // 既にインデックスがある場合は上書きしない（最初の結果を優先）
      if (!_brandIndexBox!.containsKey(normalized)) {
        await _brandIndexBox!.put(normalized, imageHash);
        debugPrint('📝 Brand index registered: $brandName → $imageHash');
      } else {
        debugPrint('ℹ️ Brand index already exists: $brandName (skipped)');
      }
    } catch (e) {
      debugPrint('⚠️ Brand index registration error: $e');
    }
  }

  /// 銘柄名の正規化
  ///
  /// スペース除去、小文字化で表記ゆれを吸収
  /// 例: "獺祭 純米大吟醸" → "獺祭純米大吟醸"
  static String _normalizeBrandName(String name) {
    return name
        .replaceAll(RegExp(r'\s+'), '') // 全角・半角スペース除去
        .toLowerCase();
  }

  /// 銘柄名インデックスをクリア（デバッグ用）
  static Future<void> clearBrandIndex() async {
    await init();
    if (_brandIndexBox == null) return;

    final count = _brandIndexBox!.length;
    await _brandIndexBox!.clear();
    debugPrint('🗑️ Brand index cleared ($count entries deleted)');
  }
}
