"""Preference scorer for ranking patterns based on user preferences."""

import numpy as np
from typing import List, Dict, Any, Optional
import logging

logger = logging.getLogger(__name__)


def build_user_vector(memory: Dict[str, Any], dim: int = 384) -> np.ndarray:
    """Build user preference vector from memory.
    
    Formula: u = mean(positive_vectors) - mean(negative_vectors)
    
    Args:
        memory: User memory dictionary with 'positive' and 'negative' lists
        dim: Dimension of the embedding vectors
        
    Returns:
        User preference vector (anchor vector)
    """
    positive = memory.get("positive", [])
    negative = memory.get("negative", [])
    
    if len(positive) == 0 and len(negative) == 0:
        return np.zeros(dim)
    
    # 计算正面向量的平均值
    if len(positive) > 0:
        pos_vectors = np.array(positive)
        mean_pos = np.mean(pos_vectors, axis=0)
    else:
        mean_pos = np.zeros(dim)
    
    # 计算负面向量的平均值
    if len(negative) > 0:
        neg_vectors = np.array(negative)
        mean_neg = np.mean(neg_vectors, axis=0)
    else:
        mean_neg = np.zeros(dim)
    
    # 用户向量 = 正面向量均值 - 负面向量均值
    user_vector = mean_pos - mean_neg
    
    return user_vector


def score_with_baseline(pattern_vec: np.ndarray, user_vec: np.ndarray) -> float:
    """Score pattern using baseline method (cosine similarity).
    
    Args:
        pattern_vec: Pattern embedding vector
        user_vec: User preference vector
        
    Returns:
        Cosine similarity score
    """
    pattern_norm = np.linalg.norm(pattern_vec)
    user_norm = np.linalg.norm(user_vec)
    
    if pattern_norm > 0 and user_norm > 0:
        similarity = np.dot(pattern_vec, user_vec) / (pattern_norm * user_norm)
    else:
        similarity = 0.0
    
    return similarity


def score_with_model(pattern_vec: np.ndarray, user_vec: np.ndarray, 
                     model: Optional[Any] = None) -> float:
    """Score pattern using learned preference encoder model.
    
    Args:
        pattern_vec: Pattern embedding vector
        user_vec: User preference vector
        model: Learned preference encoder model (PyTorch nn.Module)
        
    Returns:
        Cosine similarity score after projection
    """
    if model is None:
        # Fallback to baseline if model not available
        return score_with_baseline(pattern_vec, user_vec)
    
    try:
        import torch
        
        # 转换为torch tensor
        pattern_tensor = torch.FloatTensor(pattern_vec).unsqueeze(0)
        user_tensor = torch.FloatTensor(user_vec).unsqueeze(0)
        
        # 通过模型投影
        with torch.no_grad():
            pattern_proj = model(pattern_tensor)
            user_proj = model(user_tensor)
        
        # 计算余弦相似度
        pattern_proj_norm = torch.norm(pattern_proj, dim=1)
        user_proj_norm = torch.norm(user_proj, dim=1)
        
        if pattern_proj_norm > 0 and user_proj_norm > 0:
            similarity = torch.dot(pattern_proj.squeeze(), user_proj.squeeze()) / (
                pattern_proj_norm * user_proj_norm
            )
            similarity = similarity.item()
        else:
            similarity = 0.0
        
        return similarity
    
    except Exception as e:
        logger.warning(f"Error in model-based scoring: {e}, falling back to baseline")
        return score_with_baseline(pattern_vec, user_vec)


def score_patterns(patterns: List[Dict[str, Any]], 
                  user_vec: np.ndarray,
                  model: Optional[Any] = None,
                  alpha: float = 0.7) -> List[float]:
    """Score patterns with preference and confidence fusion.
    
    Final score = alpha * preference_score + (1 - alpha) * confidence
    
    Args:
        patterns: List of pattern dictionaries
        user_vec: User preference vector
        model: Optional learned preference encoder model
        alpha: Weight for preference score (default: 0.7)
        
    Returns:
        List of final scores
    """
    scores = []
    
    for pattern in patterns:
        # 获取模式向量（如果已计算）
        pattern_vec = pattern.get("embedding")
        
        if pattern_vec is None:
            # 如果没有预计算的向量，使用置信度作为分数
            preference_score = 0.0
        else:
            # 计算偏好分数
            if model is not None:
                preference_score = score_with_model(pattern_vec, user_vec, model)
                #print(f'已通过模型进行打分，用户偏好与{pattern}的相似度为{preference_score}')
            else:
                preference_score = score_with_baseline(pattern_vec, user_vec)
        
        # 获取置信度
        confidence = pattern.get("confidence", 0.0)
        
        # 融合分数
        final_score = alpha * preference_score + (1 - alpha) * confidence
        
        scores.append(final_score)
    
    return scores

