"""
Ranking Adjuster — Applies quality scores to recall and workflow rankings.

PRD §10 — Ranking Optimization:

§10.1 Recall Ranking upgrade:
    Original:
        semantic * 0.55 + recency * 0.20 + scene * 0.15 + scope * 0.10
    Upgraded:
        semantic * 0.45 + recency * 0.15 + scene * 0.10 + scope * 0.10
        + quality_score * 0.20

§10.2 Workflow Recommendation upgrade:
    Original:
        success_rate * 0.5 + usage * 0.3 + recency * 0.2
    Upgraded:
        success_rate * 0.4 + usage * 0.2 + recency * 0.1
        + adoption_rate * 0.2 + user_alignment * 0.1

PRD §11 — Promotion / Suppression:
    §11.1 Promotion: high adoption_rate + success_rate → boost ranking weight
    §11.2 Suppression: high noise_count + low adoption → reduce injection priority
"""

import logging
from typing import List, Dict, Optional
from openmemo.memory_feedback.feedback_store import (
    FeedbackStore, MemoryQualityProfile
)
from openmemo.memory_feedback.quality_scorer import QualityScorer

logger = logging.getLogger("openmemo.feedback")

# Promotion threshold
_PROMOTE_MIN_ADOPTION = 0.7
_PROMOTE_MIN_SUCCESS = 0.7
_PROMOTE_MIN_USAGE = 5

# Suppression threshold
_SUPPRESS_MAX_ADOPTION = 0.2
_SUPPRESS_MIN_NOISE = 5


class RankingAdjuster:
    """
    Applies memory quality feedback to recall and workflow rankings.

    Usage:
        adjuster = RankingAdjuster(store)

        # Adjust a list of recall results (each has a 'score' and 'id')
        adjusted = adjuster.adjust_recall(results, agent_id="alice")

        # Adjust workflow recommendations
        adjusted_wf = adjuster.adjust_workflows(workflows)
    """

    def __init__(self, store: FeedbackStore, scorer: QualityScorer = None):
        self.store = store
        self.scorer = scorer or QualityScorer(store)

    # ── §10.1 Recall Ranking ───────────────────────────────────────────────────

    def adjust_recall(self, results: List[Dict],
                       id_key: str = "id",
                       score_key: str = "score") -> List[Dict]:
        """
        PRD §10.1 — Re-rank recall results using quality_score.

        Upgraded formula:
            final = original * 0.80 + quality_score * 0.20

        Suppressed memories get additional penalty.
        Promoted memories get additional boost.

        results: list of dicts with at least id_key and score_key.
        Returns same list, sorted by adjusted_score descending.
        """
        if not results:
            return results

        adjusted = []
        for item in results:
            mid = item.get(id_key, "")
            original_score = float(item.get(score_key, 0.5))

            quality = 0.5  # neutral default
            action = "neutral"

            if mid:
                profile = self.store.get_quality_profile(mid)
                quality = profile.quality_score if profile.usage_count > 0 else 0.5
                action = self._get_action(profile)

            # §10.1 upgraded formula
            adj_score = original_score * 0.80 + quality * 0.20

            # §11.1 Promotion boost
            if action == "promote":
                adj_score = min(1.0, adj_score * 1.15)
            # §11.2 Suppression penalty
            elif action == "suppress":
                adj_score = adj_score * 0.70

            new_item = {**item,
                        score_key: round(adj_score, 4),
                        "original_score": original_score,
                        "quality_score": quality,
                        "ranking_action": action}
            adjusted.append(new_item)

        adjusted.sort(key=lambda x: x[score_key], reverse=True)
        logger.debug("adjust_recall: %d items re-ranked", len(adjusted))
        return adjusted

    # ── §10.2 Workflow Ranking ─────────────────────────────────────────────────

    def adjust_workflows(self, workflows: List[Dict],
                          id_key: str = "id",
                          score_key: str = "score",
                          user_alignment_scores: Dict[str, float] = None) -> List[Dict]:
        """
        PRD §10.2 — Re-rank workflow recommendations.

        Upgraded formula:
            final = success_rate * 0.4 + usage_norm * 0.2 + recency * 0.1
                  + adoption_rate * 0.2 + user_alignment * 0.1

        Falls back to: original * 0.9 + adoption_rate * 0.1 when metrics unavailable.
        """
        if not workflows:
            return workflows

        user_alignment_scores = user_alignment_scores or {}
        adjusted = []

        for wf in workflows:
            wid = wf.get(id_key, "")
            original_score = float(wf.get(score_key, 0.5))

            wf_score_data = self.scorer.score_workflow(wid)
            adoption_rate = wf_score_data.get("adoption_rate", 0.0)
            user_align = user_alignment_scores.get(wid, 0.5)

            # §10.2 upgraded formula (adoption_rate + user_alignment)
            if wf_score_data["total"] > 0:
                adj_score = (wf_score_data["enhanced_score"] * 0.9
                             + user_align * 0.1)
            else:
                adj_score = original_score * 0.9 + user_align * 0.1

            new_wf = {**wf,
                       score_key: round(min(1.0, adj_score), 4),
                       "original_score": original_score,
                       "adoption_rate": adoption_rate,
                       "user_alignment": user_align}
            adjusted.append(new_wf)

        adjusted.sort(key=lambda x: x[score_key], reverse=True)
        return adjusted

    # ── §11 Promotion / Suppression ────────────────────────────────────────────

    def _get_action(self, profile: MemoryQualityProfile) -> str:
        """
        Return promotion / suppression / neutral for a memory profile.

        §11.1 Promote: high adoption + high success + sufficient usage
        §11.2 Suppress: high noise + low adoption (after min usage)
        """
        if profile.usage_count < 3:
            return "neutral"

        if (profile.adoption_rate >= _PROMOTE_MIN_ADOPTION and
                profile.success_rate >= _PROMOTE_MIN_SUCCESS and
                profile.usage_count >= _PROMOTE_MIN_USAGE):
            return "promote"

        if (profile.noise_count >= _SUPPRESS_MIN_NOISE and
                profile.adoption_rate < _SUPPRESS_MAX_ADOPTION):
            return "suppress"

        return "neutral"

    def get_quality_score(self, memory_id: str) -> float:
        """Get current quality score for a memory (with neutral default)."""
        profile = self.store.get_quality_profile(memory_id)
        return profile.quality_score if profile.usage_count > 0 else 0.5

    def is_suppressed(self, memory_id: str) -> bool:
        """True if this memory should be downranked."""
        profile = self.store.get_quality_profile(memory_id)
        return self._get_action(profile) == "suppress"

    def is_promoted(self, memory_id: str) -> bool:
        """True if this memory should be upranked."""
        profile = self.store.get_quality_profile(memory_id)
        return self._get_action(profile) == "promote"
