import 'dart:math';
import '../models/sake_item.dart';

/// 日本酒レコメンデーションサービス
/// AIを使わずローカルで類似度計算（トークン消費0）
class SakeRecommendationService {
  /// 五味チャートのコサイン類似度を計算
  /// 戻り値: 0.0（全く異なる）〜 1.0（完全一致）
  static double calculateTasteSimilarity(
    Map<String, int> tasteA,
    Map<String, int> tasteB,
  ) {
    final keys = ['aroma', 'sweetness', 'acidity', 'bitterness', 'body'];

    double dotProduct = 0;
    double normA = 0;
    double normB = 0;

    for (var key in keys) {
      final valA = (tasteA[key] ?? 3).toDouble();
      final valB = (tasteB[key] ?? 3).toDouble();

      dotProduct += valA * valB;
      normA += valA * valA;
      normB += valB * valB;
    }

    if (normA == 0 || normB == 0) return 0;

    return dotProduct / (sqrt(normA) * sqrt(normB));
  }

  /// レコメンドスコアを計算
  ///
  /// スコアリング基準:
  /// - 同じ酒蔵: +10点
  /// - 同じ都道府県: +5点
  /// - 共通タグ: +2点/タグ
  /// - 五味類似度: +15点 × 類似度(0-1)
  static double calculateScore(SakeItem target, SakeItem candidate) {
    double score = 0;

    // 1. 酒蔵つながり
    if (candidate.displayData.displayBrewery == target.displayData.displayBrewery &&
        target.displayData.displayBrewery != '不明' &&
        candidate.id != target.id) {
      score += 10;
    }

    // 2. 都道府県つながり
    if (candidate.displayData.displayPrefecture == target.displayData.displayPrefecture &&
        target.displayData.displayPrefecture != '不明' &&
        candidate.id != target.id) {
      score += 5;
    }

    // 3. タグつながり
    final commonTags = candidate.hiddenSpecs.flavorTags
        .where((tag) => target.hiddenSpecs.flavorTags.contains(tag))
        .toList();
    score += commonTags.length * 2;

    // 4. 五味チャート類似度
    if (target.hiddenSpecs.tasteStats.isNotEmpty && candidate.hiddenSpecs.tasteStats.isNotEmpty) {
      final similarity = calculateTasteSimilarity(
        target.hiddenSpecs.tasteStats,
        candidate.hiddenSpecs.tasteStats,
      );
      score += similarity * 15;
    }

    return score;
  }

  /// レコメンド理由を生成
  static String getRecommendationReason(SakeItem target, SakeItem candidate) {
    final reasons = <String>[];

    // 酒蔵つながり
    if (candidate.displayData.displayBrewery == target.displayData.displayBrewery && target.displayData.displayBrewery != '不明') {
      reasons.add('${target.displayData.displayBrewery}つながり');
    }

    // 都道府県つながり
    if (candidate.displayData.displayPrefecture == target.displayData.displayPrefecture &&
        target.displayData.displayPrefecture != '不明' &&
        candidate.displayData.displayBrewery != target.displayData.displayBrewery) {
      reasons.add('${target.displayData.displayPrefecture}つながり');
    }

    // タグつながり
    final commonTags = candidate.hiddenSpecs.flavorTags
        .where((tag) => target.hiddenSpecs.flavorTags.contains(tag))
        .take(2)
        .toList();
    if (commonTags.isNotEmpty) {
      reasons.add(commonTags.join('・'));
    }

    // 五味類似度
    if (target.hiddenSpecs.tasteStats.isNotEmpty && candidate.hiddenSpecs.tasteStats.isNotEmpty) {
      final similarity = calculateTasteSimilarity(
        target.hiddenSpecs.tasteStats,
        candidate.hiddenSpecs.tasteStats,
      );
      if (similarity > 0.8) {
        reasons.add('似た味わい');
      }
    }

    return reasons.isEmpty ? 'おすすめ' : reasons.join(' / ');
  }

  /// スマートレコメンド（スコア順にソート）
  ///
  /// [target] 基準となる日本酒
  /// [allItems] 全日本酒リスト
  /// [limit] 返す最大件数（デフォルト: 10）
  ///
  /// 戻り値: (日本酒, スコア, 理由) のリスト
  static List<RecommendedSake> getRecommendations({
    required SakeItem target,
    required List<SakeItem> allItems,
    int limit = 10,
  }) {
    final scored = allItems
        .where((item) => item.id != target.id) // 自分自身を除外
        .map((item) {
      final score = calculateScore(target, item);
      final reason = getRecommendationReason(target, item);
      return RecommendedSake(
        item: item,
        score: score,
        reason: reason,
      );
    }).where((rec) => rec.score > 0) // スコア0より大きいもののみ
        .toList()
      ..sort((a, b) => b.score.compareTo(a.score)); // 降順ソート

    return scored.take(limit).toList();
  }
}

/// レコメンド結果
class RecommendedSake {
  final SakeItem item;
  final double score;
  final String reason;

  RecommendedSake({
    required this.item,
    required this.score,
    required this.reason,
  });
}
