"""
推荐引擎 - 智能礼物推荐算法
优化版本：多维度评分 + 个性化权重
"""
from typing import List, Dict, Optional, Tuple
from .knowledge import KnowledgeBase
from .filter import GiftFilter

class Recommender:
    """礼物推荐引擎 - 智能版"""

    # 关系权重配置：{关系: {惊喜度权重, 性价比权重, 心意指数权重}}
    RELATION_WEIGHTS = {
        # 情侣关系：重浪漫和惊喜
        "女友": {"surprise": 0.35, "value": 0.20, "sentiment": 0.45},
        "男友": {"surprise": 0.30, "value": 0.25, "sentiment": 0.45},
        "老婆": {"surprise": 0.25, "value": 0.30, "sentiment": 0.45},
        "老公": {"surprise": 0.25, "value": 0.30, "sentiment": 0.45},
        "伴侣": {"surprise": 0.30, "value": 0.25, "sentiment": 0.45},
        "喜欢的人": {"surprise": 0.35, "value": 0.20, "sentiment": 0.45},
        
        # 父母关系：重实用和心意
        "妈妈": {"surprise": 0.15, "value": 0.40, "sentiment": 0.45},
        "爸爸": {"surprise": 0.15, "value": 0.40, "sentiment": 0.45},
        "奶奶": {"surprise": 0.10, "value": 0.45, "sentiment": 0.45},
        "爷爷": {"surprise": 0.10, "value": 0.45, "sentiment": 0.45},
        "长辈": {"surprise": 0.10, "value": 0.45, "sentiment": 0.45},
        
        # 朋友关系：重惊喜和趣味
        "闺蜜": {"surprise": 0.35, "value": 0.30, "sentiment": 0.35},
        "兄弟": {"surprise": 0.35, "value": 0.30, "sentiment": 0.35},
        "朋友": {"surprise": 0.30, "value": 0.35, "sentiment": 0.35},
        "姐妹": {"surprise": 0.30, "value": 0.35, "sentiment": 0.35},
        
        # 孩子关系：重趣味和实用
        "孩子": {"surprise": 0.35, "value": 0.35, "sentiment": 0.30},
        "儿童": {"surprise": 0.35, "value": 0.35, "sentiment": 0.30},
        "学生": {"surprise": 0.25, "value": 0.40, "sentiment": 0.35},
        
        # 职场关系：重实用和体面
        "领导": {"surprise": 0.10, "value": 0.50, "sentiment": 0.40},
        "同事": {"surprise": 0.20, "value": 0.45, "sentiment": 0.35},
        "客户": {"surprise": 0.15, "value": 0.50, "sentiment": 0.35},
        "老师": {"surprise": 0.15, "value": 0.45, "sentiment": 0.40},
        
        # 默认权重
        "default": {"surprise": 0.25, "value": 0.35, "sentiment": 0.40},
    }
    
    # 场合权重调整：{场合: {惊喜度加成, 性价比加成, 心意指数加成}}
    OCCASION_BONUS = {
        "情人节": {"surprise": 0.10, "value": -0.05, "sentiment": 0.10},
        "生日": {"surprise": 0.05, "value": 0.00, "sentiment": 0.05},
        "纪念日": {"surprise": 0.08, "value": -0.03, "sentiment": 0.08},
        "结婚纪念日": {"surprise": 0.08, "value": -0.03, "sentiment": 0.10},
        "母亲节": {"surprise": 0.00, "value": 0.05, "sentiment": 0.10},
        "父亲节": {"surprise": 0.00, "value": 0.05, "sentiment": 0.10},
        "春节": {"surprise": -0.05, "value": 0.10, "sentiment": 0.05},
        "中秋节": {"surprise": -0.05, "value": 0.10, "sentiment": 0.05},
        "圣诞节": {"surprise": 0.10, "value": 0.00, "sentiment": 0.05},
        "商务": {"surprise": -0.10, "value": 0.15, "sentiment": -0.05},
        "道歉": {"surprise": 0.00, "value": 0.00, "sentiment": 0.15},
        "表白": {"surprise": 0.15, "value": -0.05, "sentiment": 0.15},
        "婚礼": {"surprise": -0.05, "value": 0.10, "sentiment": 0.05},
        "乔迁": {"surprise": 0.00, "value": 0.10, "sentiment": 0.00},
        "看望": {"surprise": -0.05, "value": 0.10, "sentiment": 0.05},
        "default": {"surprise": 0.00, "value": 0.00, "sentiment": 0.00},
    }
    
    # 风格惊喜度映射
    STYLE_SURPRISE_FACTOR = {
        "惊喜型": 1.5,
        "浪漫型": 1.2,
        "酷炫型": 1.3,
        "有趣型": 1.2,
        "意义型": 1.1,
        "奢华型": 1.4,
        "休闲型": 0.9,
        "实用型": 0.7,
        "健康型": 0.8,
        "传统型": 0.6,
        "正式型": 0.7,
        "经济型": 0.6,
        "default": 1.0,
    }

    def __init__(self):
        self.kb = KnowledgeBase()
        self.gift_filter = GiftFilter()
        
        # 统计礼物出现频率（用于惊喜度计算）
        self._gift_frequency = self._calculate_gift_frequency()

    def _calculate_gift_frequency(self) -> Dict[str, int]:
        """统计礼物在不同场景中的出现频率"""
        frequency = {}
        for scenario in self.kb.get_all_scenarios():
            for gift in scenario.get("gifts", []):
                name = gift.get("name", "")
                frequency[name] = frequency.get(name, 0) + 1
        return frequency

    def _get_relation_weights(self, relation: Optional[str]) -> Dict[str, float]:
        """获取关系对应的权重配置"""
        if not relation:
            return self.RELATION_WEIGHTS["default"]
        
        # 精确匹配
        if relation in self.RELATION_WEIGHTS:
            return self.RELATION_WEIGHTS[relation].copy()
        
        # 模糊匹配
        for key in self.RELATION_WEIGHTS:
            if key in relation or relation in key:
                return self.RELATION_WEIGHTS[key].copy()
        
        return self.RELATION_WEIGHTS["default"]

    def _get_occasion_bonus(self, occasion: Optional[str]) -> Dict[str, float]:
        """获取场合对应的权重加成"""
        if not occasion:
            return self.OCCASION_BONUS["default"]
        
        # 精确匹配
        if occasion in self.OCCASION_BONUS:
            return self.OCCASION_BONUS[occasion].copy()
        
        # 模糊匹配
        for key in self.OCCASION_BONUS:
            if key in occasion or occasion in key:
                return self.OCCASION_BONUS[key].copy()
        
        return self.OCCASION_BONUS["default"]

    def _calculate_surprise_score(
        self, 
        gift: Dict, 
        scenario: Dict,
        all_gifts: List[Dict]
    ) -> float:
        """
        计算惊喜度评分 (0-100)
        
        考虑因素：
        1. 礼物稀缺性（出现频率低 = 更惊喜）
        2. 风格惊喜因子
        3. 价格惊喜度（适中预算下的高价值感）
        """
        score = 50.0  # 基础分
        
        # 1. 稀缺性加分（出现频率越低越惊喜）
        gift_name = gift.get("name", "")
        frequency = self._gift_frequency.get(gift_name, 1)
        max_frequency = max(self._gift_frequency.values()) if self._gift_frequency else 1
        scarcity_bonus = (1 - frequency / max_frequency) * 20
        score += scarcity_bonus
        
        # 2. 风格惊喜因子
        style = scenario.get("style", "default")
        surprise_factor = self.STYLE_SURPRISE_FACTOR.get(style, 1.0)
        style_bonus = (surprise_factor - 1.0) * 30
        score += style_bonus
        
        # 3. 价格惊喜度（中等价位且高价值感）
        price = gift.get("price", 0)
        budget_range = scenario.get("budget_range", [0, 1000])
        budget_center = (budget_range[0] + budget_range[1]) / 2
        
        # 价格在预算中位数附近且有惊喜感
        if budget_range[0] <= price <= budget_range[1]:
            price_ratio = abs(price - budget_center) / (budget_range[1] - budget_range[0] + 1)
            # 稍高于中位数更有惊喜感
            if price > budget_center:
                score += min(10, (price - budget_center) / budget_center * 100)
        
        # 4. 理由中的惊喜关键词
        reason = gift.get("reason", "")
        surprise_keywords = ["惊喜", "特别", "独特", "限量", "定制", "珍藏", "独家"]
        keyword_bonus = sum(5 for kw in surprise_keywords if kw in reason)
        score += min(15, keyword_bonus)
        
        return min(100, max(0, score))

    def _calculate_value_score(
        self,
        gift: Dict,
        budget_min: Optional[int],
        budget_max: Optional[int]
    ) -> float:
        """
        计算性价比评分 (0-100)
        
        考虑因素：
        1. 预算利用率（在预算范围内得分更高）
        2. 价格合理性
        3. 预算匹配度
        """
        score = 50.0
        price = gift.get("price", 0)
        
        if not budget_min and not budget_max:
            # 没有预算限制，默认性价比
            return 60.0
        
        budget_min = budget_min or 0
        budget_max = budget_max or price * 2
        budget_center = (budget_min + budget_max) / 2
        budget_range = budget_max - budget_min
        
        # 1. 预算内得分
        if budget_min <= price <= budget_max:
            score += 25
            
            # 2. 预算利用率（接近预算上限但不过度）
            utilization = price / budget_max if budget_max > 0 else 0
            if 0.6 <= utilization <= 0.9:
                # 最佳利用率区间
                score += 15
            elif 0.5 <= utilization <= 0.95:
                score += 10
        else:
            # 预算外扣分
            if price < budget_min:
                # 低于预算
                score -= min(20, (budget_min - price) / budget_min * 50)
            elif price > budget_max:
                # 超出预算（允许一定超出）
                over_ratio = (price - budget_max) / budget_max
                if over_ratio <= 0.2:
                    score -= 10
                else:
                    score -= min(30, over_ratio * 50)
        
        # 3. 价格合理性（避免极端价格）
        if 50 <= price <= 2000:
            score += 10
        
        return min(100, max(0, score))

    def _calculate_sentiment_score(
        self,
        gift: Dict,
        scenario: Dict,
        relation: Optional[str]
    ) -> float:
        """
        计算心意指数评分 (0-100)
        
        考虑因素：
        1. 关系匹配度
        2. 场合适配度
        3. 理由中的情感关键词
        4. 个性化程度
        """
        score = 50.0
        
        # 1. 关系匹配度
        scenario_relation = scenario.get("relation", "")
        if relation and scenario_relation:
            if relation == scenario_relation:
                score += 20
            elif relation in scenario_relation or scenario_relation in relation:
                score += 15
            # 关系类型匹配（如：女友->情侣类）
            relation_type_match = self._match_relation_type(relation, scenario_relation)
            if relation_type_match:
                score += 10
        
        # 2. 场合适配度
        scenario_occasion = scenario.get("occasion", "")
        # 场景的occasion已经在前置筛选中匹配，这里加分
        score += 15
        
        # 3. 理由中的情感关键词
        reason = gift.get("reason", "")
        sentiment_keywords = [
            "爱", "心", "情", "意", "关怀", "温暖", "贴心", "珍贵",
            "纪念", "祝福", "感恩", "孝心", "诚意", "用心"
        ]
        keyword_bonus = sum(3 for kw in sentiment_keywords if kw in reason)
        score += min(15, keyword_bonus)
        
        # 4. 风格与关系匹配
        style = scenario.get("style", "")
        relation_style_match = self._match_relation_style(relation, style)
        score += relation_style_match * 10
        
        return min(100, max(0, score))

    def _match_relation_type(self, relation1: str, relation2: str) -> bool:
        """判断两个关系是否属于同一类型"""
        type_groups = [
            {"女友", "男友", "老婆", "老公", "伴侣", "喜欢的人"},
            {"妈妈", "爸爸", "奶奶", "爷爷", "长辈"},
            {"闺蜜", "兄弟", "朋友", "姐妹"},
            {"孩子", "儿童", "学生"},
            {"领导", "同事", "客户", "老师"},
        ]
        
        for group in type_groups:
            if relation1 in group and relation2 in group:
                return True
        return False

    def _match_relation_style(self, relation: Optional[str], style: str) -> float:
        """判断风格与关系的匹配度"""
        if not relation:
            return 0.5
        
        # 关系-风格匹配表
        relation_style_scores = {
            "女友": {"浪漫型": 1.0, "惊喜型": 0.9, "美丽型": 0.8},
            "男友": {"酷炫型": 1.0, "实用型": 0.8, "科技型": 0.9},
            "老婆": {"浪漫型": 0.9, "意义型": 1.0, "奢华型": 0.8},
            "老公": {"实用型": 1.0, "科技型": 0.8},
            "妈妈": {"健康型": 1.0, "实用型": 0.9, "美丽型": 0.8},
            "爸爸": {"实用型": 1.0, "科技型": 0.8, "健康型": 0.9},
            "朋友": {"有趣型": 1.0, "实用型": 0.8},
            "孩子": {"趣味型": 1.0, "教育型": 0.9},
            "领导": {"正式型": 1.0, "敬意型": 0.9},
        }
        
        for rel_key, style_scores in relation_style_scores.items():
            if rel_key in relation or relation in rel_key:
                return style_scores.get(style, 0.5)
        
        return 0.5

    def _calculate_final_score(
        self,
        gift: Dict,
        scenario: Dict,
        relation: Optional[str],
        occasion: Optional[str],
        budget_min: Optional[int],
        budget_max: Optional[int],
        all_gifts: List[Dict]
    ) -> Tuple[float, Dict[str, float]]:
        """
        计算最终综合评分
        
        Returns:
            (总分, 各维度得分字典)
        """
        # 获取基础权重
        weights = self._get_relation_weights(relation)
        
        # 应用场合加成
        occasion_bonus = self._get_occasion_bonus(occasion)
        for key in weights:
            weights[key] = max(0, min(1, weights[key] + occasion_bonus.get(key, 0)))
        
        # 归一化权重
        total_weight = sum(weights.values())
        if total_weight > 0:
            weights = {k: v / total_weight for k, v in weights.items()}
        
        # 计算各维度得分
        surprise_score = self._calculate_surprise_score(gift, scenario, all_gifts)
        value_score = self._calculate_value_score(gift, budget_min, budget_max)
        sentiment_score = self._calculate_sentiment_score(gift, scenario, relation)
        
        # 计算加权总分
        final_score = (
            weights["surprise"] * surprise_score +
            weights["value"] * value_score +
            weights["sentiment"] * sentiment_score
        )
        
        scores = {
            "surprise": round(surprise_score, 1),
            "value": round(value_score, 1),
            "sentiment": round(sentiment_score, 1),
            "weights": weights,
        }
        
        return round(final_score, 2), scores

    def recommend(
        self,
        relation: Optional[str] = None,
        occasion: Optional[str] = None,
        budget_min: Optional[int] = None,
        budget_max: Optional[int] = None,
        limit: int = 5
    ) -> List[Dict]:
        """
        智能推荐礼物
        
        Args:
            relation: 收礼人关系
            occasion: 场合
            budget_min: 最低预算
            budget_max: 最高预算
            limit: 返回数量
            
        Returns:
            推荐礼物列表，每个礼物包含评分信息
        """
        # 1. 从知识库筛选场景
        scenarios = self.kb.filter_scenarios(
            relation=relation,
            occasion=occasion,
        )

        if not scenarios:
            return []

        # 2. 收集所有礼物并计算评分
        scored_gifts = []
        
        for scenario in scenarios:
            for gift in scenario.get("gifts", []):
                # 预算过滤（允许上下浮动50%）
                price = gift.get("price", 0)
                if budget_min and price < budget_min * 0.5:
                    continue
                if budget_max and price > budget_max * 1.5:
                    continue

                # 计算综合评分
                final_score, scores = self._calculate_final_score(
                    gift, scenario, relation, occasion,
                    budget_min, budget_max,
                    []  # 稍后传入all_gifts
                )

                # 构建礼物信息
                gift_info = {
                    **gift,
                    "scenario_id": scenario["id"],
                    "scenario_relation": scenario.get("relation"),
                    "scenario_occasion": scenario.get("occasion"),
                    "scenario_style": scenario.get("style"),
                    "final_score": final_score,
                    "scores": scores,
                }
                scored_gifts.append(gift_info)

        # 3. 禁忌过滤
        filtered_gifts = self.gift_filter.filter(scored_gifts, relation)

        # 4. 去重（按名称，保留评分最高的）
        unique_gifts = {}
        for gift in filtered_gifts:
            name = gift.get("name")
            if name:
                if name not in unique_gifts or gift["final_score"] > unique_gifts[name]["final_score"]:
                    unique_gifts[name] = gift

        # 5. 按综合评分排序
        sorted_gifts = sorted(
            unique_gifts.values(),
            key=lambda g: g["final_score"],
            reverse=True
        )

        # 6. 限制数量
        result = sorted_gifts[:limit]
        
        # 7. 添加排名
        for i, gift in enumerate(result, 1):
            gift["rank"] = i

        return result
