"""
Experience Registry — Phase 23.6

Stores, retrieves, and ranks Experience records and WorkflowTemplates.

Provides:
  - save(experience) / save_workflow(workflow)
  - get(exp_id) / get_workflow(wf_id)
  - list_experiences() / list_workflows()
  - recall(query, scene) — find relevant experiences for a query
  - record_workflow_result(wf_id, success) — update success_rate

Uses SQLiteStore's experiences + workflow_templates tables.
"""

import logging
import time
from typing import Dict, List, Optional

from openmemo.experience.models import Experience, WorkflowTemplate

logger = logging.getLogger("openmemo.experience.registry")


class ExperienceRegistry:
    """
    CRUD + ranked recall for Experiences and WorkflowTemplates.

    Args:
        store — SQLiteStore instance
    """

    def __init__(self, store):
        self._store = store

    # ─── Experience CRUD ───────────────────────────────────────────

    def save(self, experience: Experience) -> str:
        """Persist an experience. Returns experience ID."""
        self._store.put_experience(experience.to_dict())
        return experience.id

    def save_many(self, experiences: List[Experience]) -> List[str]:
        """Persist a list of experiences."""
        return [self.save(e) for e in experiences]

    def get(self, exp_id: str) -> Optional[Experience]:
        data = self._store.get_experience(exp_id)
        if not data:
            return None
        return Experience.from_dict(data)

    def list_experiences(self, agent_id: str = "", scene: str = "",
                         exp_type: str = "", limit: int = 100) -> List[Experience]:
        rows = self._store.list_experiences(
            agent_id=agent_id, scene=scene,
            exp_type=exp_type, limit=limit,
        )
        return [Experience.from_dict(r) for r in rows]

    def delete(self, exp_id: str) -> bool:
        return self._store.delete_experience(exp_id)

    # ─── Workflow CRUD ─────────────────────────────────────────────

    def save_workflow(self, workflow: WorkflowTemplate) -> str:
        """Persist a workflow template. Returns workflow ID."""
        self._store.put_workflow_template(workflow.to_dict())
        return workflow.id

    def save_workflows(self, workflows: List[WorkflowTemplate]) -> List[str]:
        return [self.save_workflow(w) for w in workflows]

    def get_workflow(self, wf_id: str) -> Optional[WorkflowTemplate]:
        data = self._store.get_workflow_template(wf_id)
        if not data:
            return None
        return WorkflowTemplate.from_dict(data)

    def list_workflows(self, agent_id: str = "", scene: str = "",
                       limit: int = 50) -> List[WorkflowTemplate]:
        rows = self._store.list_workflow_templates(
            agent_id=agent_id, scene=scene, limit=limit,
        )
        return [WorkflowTemplate.from_dict(r) for r in rows]

    def record_workflow_result(self, wf_id: str, success: bool) -> bool:
        """Update success_rate + usage_count for a workflow."""
        wf = self.get_workflow(wf_id)
        if not wf:
            return False
        wf.usage_count += 1
        total = wf.usage_count
        prev_rate = wf.success_rate
        wf.success_rate = ((prev_rate * (total - 1)) + (1.0 if success else 0.0)) / total
        self._store.put_workflow_template(wf.to_dict())
        return True

    # ─── Recall ────────────────────────────────────────────────────

    def recall(self, query: str, scene: str = "",
               agent_id: str = "", top_k: int = 5) -> List[Experience]:
        """
        Find relevant experiences for a query.

        Uses keyword + scene + recency + confidence ranking.
        Increments usage_count on retrieved experiences.
        """
        all_experiences = self.list_experiences(
            agent_id=agent_id, scene=scene, limit=500
        )
        if not all_experiences:
            all_experiences = self.list_experiences(limit=500)

        ranked = self._rank_experiences(all_experiences, query, scene)[:top_k]

        for exp in ranked:
            exp.usage_count += 1
            self._store.put_experience(exp.to_dict())

        return ranked

    def recall_workflows(self, query: str, scene: str = "",
                         agent_id: str = "", top_k: int = 3,
                         user_id: str = "",
                         user_memory=None) -> List[WorkflowTemplate]:
        """
        Find relevant workflow templates for a query.

        PRD §10 — When user_id + user_memory provided, applies User Alignment Score:
            final_score = workflow_score * 0.7 + user_alignment_score * 0.3
        """
        all_workflows = self.list_workflows(
            agent_id=agent_id, scene=scene, limit=200
        )
        if not all_workflows:
            all_workflows = self.list_workflows(limit=200)

        ranked = self._rank_workflows(all_workflows, query, scene)

        # §10 — User Memory fusion: re-rank using user alignment score
        if user_id and user_memory is not None:
            try:
                wf_dicts = []
                for wf in ranked:
                    steps = wf.steps if hasattr(wf, "steps") else []
                    wf_dicts.append({
                        "_wf_obj": wf,
                        "description": getattr(wf, "description", ""),
                        "steps": steps,
                        "score": getattr(wf, "confidence", 0.5),
                    })
                re_ranked = user_memory.rank_workflows(
                    user_id, wf_dicts, score_key="score"
                )
                ranked = [d["_wf_obj"] for d in re_ranked]
            except Exception:
                pass  # silently fall back to original ranking

        return ranked[:top_k]

    def recall_for_prompt(self, query: str, scene: str = "",
                          agent_id: str = "", top_k: int = 3) -> str:
        """
        Returns prompt-ready experience context.
        Prioritizes workflows > experiences.
        """
        workflows = self.recall_workflows(query, scene=scene,
                                          agent_id=agent_id, top_k=2)
        experiences = self.recall(query, scene=scene,
                                  agent_id=agent_id, top_k=top_k)

        parts = []
        if workflows:
            parts.append("## Relevant Workflows")
            for wf in workflows:
                parts.append(wf.to_prompt_text())

        if experiences:
            parts.append("## Relevant Experiences")
            for exp in experiences:
                parts.append(exp.to_prompt_text())

        return "\n\n".join(parts)

    # ─── Stats ─────────────────────────────────────────────────────

    def stats(self, agent_id: str = "", scene: str = "") -> Dict:
        experiences = self.list_experiences(
            agent_id=agent_id, scene=scene, limit=10000
        )
        workflows = self.list_workflows(
            agent_id=agent_id, scene=scene, limit=10000
        )
        type_counts: Dict[str, int] = {}
        for e in experiences:
            type_counts[e.type] = type_counts.get(e.type, 0) + 1

        avg_confidence = (
            sum(e.confidence for e in experiences) / len(experiences)
            if experiences else 0.0
        )
        return {
            "total_experiences": len(experiences),
            "total_workflows": len(workflows),
            "by_type": type_counts,
            "avg_confidence": round(avg_confidence, 3),
            "agent_id": agent_id,
            "scene": scene,
        }

    # ─── Ranking helpers ───────────────────────────────────────────

    def _rank_experiences(self, experiences: List[Experience],
                          query: str, scene: str) -> List[Experience]:
        query_terms = set(query.lower().split())
        now = time.time()

        def score(exp: Experience) -> float:
            text = (exp.title + " " + exp.outcome + " " +
                    " ".join(exp.steps) + " " +
                    exp.conditions.get("trigger", "")).lower()
            kw = sum(1.0 for t in query_terms if t in text) / max(len(query_terms), 1)
            scene_bonus = 0.2 if (scene and exp.scene == scene) else 0.0
            age_days = (now - exp.created_at) / 86400
            recency = max(0.0, 1.0 - age_days / 30)
            usage_bonus = min(0.1, exp.usage_count * 0.01)
            return kw * 0.5 + exp.confidence * 0.2 + scene_bonus + recency * 0.1 + usage_bonus

        return sorted(experiences, key=score, reverse=True)

    def _rank_workflows(self, workflows: List[WorkflowTemplate],
                        query: str, scene: str) -> List[WorkflowTemplate]:
        query_terms = set(query.lower().split())
        now = time.time()

        def score(wf: WorkflowTemplate) -> float:
            text = (wf.name + " " + wf.trigger + " " +
                    " ".join(
                        s.get("step", "") if isinstance(s, dict) else str(s)
                        for s in wf.steps
                    )).lower()
            kw = sum(1.0 for t in query_terms if t in text) / max(len(query_terms), 1)
            scene_bonus = 0.2 if (scene and wf.scene == scene) else 0.0
            success_bonus = wf.success_rate * 0.2
            age_days = (now - wf.created_at) / 86400
            recency = max(0.0, 1.0 - age_days / 30)
            return kw * 0.4 + success_bonus + scene_bonus + recency * 0.1

        return sorted(workflows, key=score, reverse=True)
