import '../models/sake_item.dart';
import '../models/mbti_result.dart';
import 'mbti_types.dart';

class MBTIDiagnosisService {
  MBTIResult diagnose(List<SakeItem> items) {
    if (items.isEmpty) {
      return MBTIResult.insufficient(0);
    }

    // 1. Calculate Scores for each Axis (0.0 to 1.0)
    // > 0.5 leans towards the Left (E, S, T, J)
    // <= 0.5 leans towards the Right (I, N, F, P)
    // We can adjust thresholds if needed.

    final double eScore = _calculateEScore(items); // E vs I (Social)
    final double sScore = _calculateSScore(items); // S vs N (Recognition)
    final double tScore = _calculateTScore(items); // T vs F (Judgment)
    final double jScore = _calculateJScore(items); // J vs P (Tactics)

    // 2. Determine Axis Values
    final bool isE = eScore >= 0.5;
    final bool isS = sScore >= 0.5;
    final bool isT = tScore >= 0.5;
    final bool isJ = jScore >= 0.5;

    // 3. Construct 4-letter code
    final String code = [
      isE ? 'E' : 'I',
      isS ? 'S' : 'N',
      isT ? 'T' : 'F',
      isJ ? 'J' : 'P',
    ].join();

    // 4. Retrieve Type
    final type = MBTIType.types[code] ?? MBTIType.unknown();

    // 5. Calculate Confidence (Simple Average of "distance from 0.5")
    // e.g. Score 0.9 -> distance 0.4. Score 0.51 -> distance 0.01.
    // Max distance is 0.5. So (dist / 0.5) * 100 is % confidence for that axis.
    final double avgDist = (
      ((eScore - 0.5).abs()) +
      ((sScore - 0.5).abs()) +
      ((tScore - 0.5).abs()) +
      ((jScore - 0.5).abs())
    ) / 4.0;
    
    // Normalize: Max possible sum of diffs is 0.5 * 4 = 2.0.
    // So avgDist max is 0.5. 
    // Confidence = avgDist * 2. (0.5 * 2 = 1.0)
    final double confidence = avgDist * 2.0;

    return MBTIResult(
      type: type,
      sampleSize: items.length,
      confidence: confidence,
      axisScores: {
        'E/I': isE,
        'S/N': isS,
        'T/F': isT,
        'J/P': isJ,
      },
    );
  }

  // --- Axis Calculation Logic ---

  /// E (Extrovert) vs I (Introvert)
  /// Logic: Ratio of 'Set/Menu' items to total items.
  /// If user drinks "Sets" (Drinking comparison), they are likely 'Social/Party' (E).
  /// If user drinks "Single" items mainly, they are 'Solitary' (I).
  /// Threshold: Sets are rarer, so if > 10% are sets, lean E.
  double _calculateEScore(List<SakeItem> items) {
    if (items.isEmpty) return 0.5; // Phase B fix: neutral default

    int setOrderCount = items.where((item) => item.itemType == ItemType.set).length;
    double ratio = setOrderCount / items.length;

    // Phase B fix: Neutral baseline (0.5) instead of I-biased (0.3)
    // Map 0.0 -> 0.5 (Neutral)
    // Map 0.1 -> 0.6 (Slight E)
    // Map 0.2 -> 0.7 (Strong E)

    // Formula: score = 0.5 + (ratio * 1.0)
    double score = 0.5 + ratio;
    return score.clamp(0.0, 1.0);
  }

  /// S (Sensing) vs N (Intuition)
  /// Logic: "Specs" vs "Vibes".
  /// S: Detailed numeric specs (SMV, Polishing, Alcohol, Rice, Yeast).
  /// N: Photos, Emotional Tags, Empty specs.
  double _calculateSScore(List<SakeItem> items) {
     if (items.isEmpty) return 0.5;

     // Logic combined into the loop below.

     // Average fill rate of technical specs.
     // If user fills 3/5 avg, they are S.
     // If user fills 0/5 avg, they are N.
     double debugTotalSpecFills = 0;
     int maxPossibleSpecs = items.length * 5;

     for (var item in items) {
        final h = item.hiddenSpecs;
        if (h.sakeMeterValue != null) debugTotalSpecFills++;
        if (h.polishingRatio != null) debugTotalSpecFills++;
        if (h.alcoholContent != null) debugTotalSpecFills++;
        if (h.yeast != null && h.yeast!.isNotEmpty) debugTotalSpecFills++;
        if (h.riceVariety != null && h.riceVariety!.isNotEmpty) debugTotalSpecFills++;
     }

     double fillRatio = maxPossibleSpecs == 0 ? 0 : debugTotalSpecFills / maxPossibleSpecs;

     // Phase B fix: Neutral baseline (0.5) instead of N-biased (0.2)
     // Map 0.0 -> 0.5 (Neutral)
     // Map 0.3 -> 0.65 (Slight S)
     // Map 0.6 -> 0.8 (Strong S)
     // Map 1.0 -> 1.0 (Max S)

     // Formula: score = 0.5 + (fillRatio * 0.5)
     double score = 0.5 + (fillRatio * 0.5);
     return score.clamp(0.0, 1.0);
  }

  /// T (Thinking) vs F (Feeling)
  /// Logic: "Logic" vs "Emotion".
  /// T: Strict rating, more critical? Or just low favorites?
  /// F: High favorite ratio. "Love everything".
  double _calculateTScore(List<SakeItem> items) {
    if (items.isEmpty) return 0.5;
    
    int favoriteCount = items.where((i) => i.userData.isFavorite).length;
    double favoriteRatio = favoriteCount / items.length;

    // Phase B fix: Neutral baseline (0.5) instead of extreme T-bias (1.0)
    // Map 0.0 -> 0.5 (Neutral)
    // Map 0.5 -> 0.25 (Slight F)
    // Map 1.0 -> 0.0 (Strong F)

    // High Favorite -> F (Low T score)
    // Low Favorite -> Neutral (T score 0.5)

    // Formula: score = 0.5 - (favoriteRatio * 0.5)
    return 0.5 - (favoriteRatio * 0.5); 
  }

  /// J (Judging) vs P (Prospecting)
  /// Logic: "Planned/Completeness" vs "Random".
  /// J: High data completeness (user input fields), Organized.
  /// P: Low completeness (just photo and name), Spontaneous.
  /// Using "Input Completeness" as a proxy for J.
  /// Similar to S/N but includes subjective fields like Memo/Price/Location?
  /// Or "Prefecture Coverage"?
  /// Let's use "Input Completeness of REQUIRED BASIC fields" (Name, Brand, Prefecture)
  /// Almost everyone fills Name/Brand.
  /// Let's use "Has Memo" and "Has Price" and "Has Date".
  /// J types likely fill Price and Memo.
  double _calculateJScore(List<SakeItem> items) {
    if (items.isEmpty) return 0.5;
    
    int detailedItemCount = 0;
    
    for (var item in items) {
      bool hasMemo = item.userData.memo != null && item.userData.memo!.isNotEmpty;
      bool hasPrice = item.userData.price != null || item.userData.costPrice != null;
      // bool hasPrefecture = item.displayData.displayPrefecture != 'Unknown'; // Default is often unknown
      
      if (hasMemo && hasPrice) {
        detailedItemCount++;
      }
    }
    
    double detailedRatio = detailedItemCount / items.length;

    // Phase B fix: Neutral baseline (0.5) instead of P-biased (0.2)
    // Map 0.0 -> 0.5 (Neutral)
    // Map 0.5 -> 0.75 (Slight J)
    // Map 1.0 -> 1.0 (Strong J)

    // If user inputs price AND memo for > 50% items -> J.
    // Else -> Neutral.

    // Formula: score = 0.5 + (detailedRatio * 0.5)
    double score = 0.5 + (detailedRatio * 0.5);
    return score.clamp(0.0, 1.0);
  }
}
