import '../models/sake_item.dart';
import '../models/sakenowa/sakenowa_models.dart';
import 'sakenowa_service.dart';
import 'dart:math' as math;

/// さけのわ類似推薦サービス
///
/// ユーザーの特定の日本酒に類似したさけのわランキング銘柄を推薦
/// 「この銘柄が好きなら、こちらもおすすめ」形式
class SakenowaSimilarRecommendationService {
  final SakenowaService _sakenowaService;

  SakenowaSimilarRecommendationService(this._sakenowaService);

  /// 類似銘柄を推薦
  ///
  /// [baseSake]: 基準となる日本酒
  /// [limit]: 推薦数（デフォルト: 5）
  /// [excludeOwned]: ユーザーが既に持っている銘柄を除外するか（デフォルト: true）
  /// [userItems]: ユーザーの全日本酒リスト（除外判定用）
  Future<List<SimilarSakeRecommendation>> getSimilarFromSakenowa({
    required SakeItem baseSake,
    int limit = 5,
    bool excludeOwned = true,
    List<SakeItem>? userItems,
  }) async {
    // さけのわデータ取得
    final rankings = await _sakenowaService.getRankings();
    final brands = await _sakenowaService.getBrands();
    final flavorCharts = await _sakenowaService.getFlavorCharts();

    // マップ化
    final brandMap = {for (var b in brands) b.id: b};
    final chartMap = {for (var c in flavorCharts) c.brandId: c};

    // 既飲銘柄名セット（小文字変換）
    final ownedNames = userItems != null
        ? userItems.map((i) => i.displayData.displayName.toLowerCase()).toSet()
        : <String>{};

    // baseSakeの味覚データ取得（6軸優先）
    final baseTaste = baseSake.hiddenSpecs.activeTasteData;

    // ランキングとの類似度計算
    final scoredRankings = <SimilarSakeRecommendation>[];

    for (final ranking in rankings.take(100)) {
      final brand = brandMap[ranking.brandId];
      final chart = chartMap[ranking.brandId];

      if (brand == null || chart == null) continue;

      // 除外判定
      if (excludeOwned && ownedNames.contains(brand.name.toLowerCase())) {
        continue; // 既に持っている銘柄はスキップ
      }

      // 基準銘柄自身もスキップ
      if (brand.name.toLowerCase() == baseSake.displayData.displayName.toLowerCase()) {
        continue;
      }

      // さけのわフレーバーを五味に変換
      final targetTaste = chart.toFiveAxisTaste();

      // 類似度計算（コサイン類似度）
      final similarity = _calculateCosineSimilarity(baseTaste, targetTaste);

      scoredRankings.add(SimilarSakeRecommendation(
        ranking: ranking,
        brand: brand,
        flavorChart: chart,
        similarityScore: similarity,
        reason: _generateReason(baseSake, chart, similarity),
      ));
    }

    // 類似度でソート（降順）
    scoredRankings.sort((a, b) => b.similarityScore.compareTo(a.similarityScore));

    // 上位N件を返す
    return scoredRankings.take(limit).toList();
  }

  /// コサイン類似度計算
  ///
  /// 2つの味覚ベクトルの類似度を0.0-1.0で返す
  /// 1.0に近いほど類似
  double _calculateCosineSimilarity(
    Map<String, double> taste1,
    Map<String, double> taste2,
  ) {
    // 共通の軸のみ使用
    final keys = taste1.keys.toSet().intersection(taste2.keys.toSet());
    if (keys.isEmpty) return 0.0;

    double dotProduct = 0.0;
    double magnitude1 = 0.0;
    double magnitude2 = 0.0;

    for (final key in keys) {
      final v1 = taste1[key] ?? 0.0;
      final v2 = taste2[key] ?? 0.0;

      dotProduct += v1 * v2;
      magnitude1 += v1 * v1;
      magnitude2 += v2 * v2;
    }

    final magnitude = math.sqrt(magnitude1) * math.sqrt(magnitude2);
    if (magnitude == 0) return 0.0;

    return (dotProduct / magnitude).clamp(0.0, 1.0);
  }

  /// 推薦理由生成
  String _generateReason(
    SakeItem baseSake,
    SakenowaFlavorChart targetChart,
    double similarity,
  ) {
    // baseSakeの五味チャート
    final baseTaste = baseSake.hiddenSpecs.activeTasteData;

    // targetの五味チャート
    final targetTaste = targetChart.toFiveAxisTaste();

    // 最も類似している軸を2つ抽出
    final similarities = <String, double>{};
    for (final key in baseTaste.keys) {
      final diff = (baseTaste[key]! - (targetTaste[key] ?? 0.5)).abs();
      similarities[key] = 1.0 - diff; // 差が小さいほど類似度高い
    }

    final sortedAxes = similarities.entries.toList()
      ..sort((a, b) => b.value.compareTo(a.value));

    final topAxes = sortedAxes.take(2).toList();

    // 日本語ラベル
    const axisLabels = {
      'aroma': '香り',
      'sweetness': '甘み',
      'acidity': '酸味',
      'bitterness': 'キレ',
      'body': 'コク',
    };

    if (topAxes.length >= 2) {
      final label1 = axisLabels[topAxes[0].key] ?? topAxes[0].key;
      final label2 = axisLabels[topAxes[1].key] ?? topAxes[1].key;
      return '$label1と$label2が似ています';
    } else if (topAxes.length == 1) {
      final label1 = axisLabels[topAxes[0].key] ?? topAxes[0].key;
      return '$label1が似ています';
    }

    return '味わいが似ています';
  }
}

/// 類似銘柄推薦結果
class SimilarSakeRecommendation {
  final SakenowaRanking ranking;
  final SakenowaBrand brand;
  final SakenowaFlavorChart flavorChart;
  final double similarityScore; // 0.0-1.0
  final String reason;

  SimilarSakeRecommendation({
    required this.ranking,
    required this.brand,
    required this.flavorChart,
    required this.similarityScore,
    required this.reason,
  });

  /// パーセント表示
  int get similarityPercent => (similarityScore * 100).round();
}
