"""
Memory Profile Builder — Builds quality profile for Inspector.

PRD §4.2 — Memory Profile output:
{
    "memory_id":     "mem_123",
    "memory_layer":  "experience",
    "usage_count":   18,
    "adoption_rate": 0.72,
    "success_rate":  0.68,
    "quality_score": 0.74,
    "noise_count":   3,
    "last_used":     "2026-03-30T10:10:00Z",
    "status":        "promoted" | "suppressed" | "normal"
}

PRD §5.2 — Calculation:
    usage_count   = recalled_count
    adoption_rate = adopted / recalled
    success_rate  = success / adopted
"""

import time
import logging
from typing import List, Optional
from openmemo.feedback_aggregator.utils import to_iso, safe_div

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

# Thresholds mirroring ranking_adjuster
_PROMOTE_MIN_ADOPTION = 0.7
_PROMOTE_MIN_SUCCESS  = 0.7
_PROMOTE_MIN_USAGE    = 5
_SUPPRESS_MAX_ADOPTION = 0.2
_SUPPRESS_MIN_NOISE   = 5


class MemoryProfileBuilder:
    """
    Builds a structured quality profile for a single memory.

    Usage:
        builder = MemoryProfileBuilder(feedback_store)
        profile = builder.build("mem_123")
    """

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

    def build(self, memory_id: str) -> dict:
        """PRD §5.2 — Build memory profile for Inspector."""
        t0 = time.time()

        # Load raw profile from quality_profiles table
        raw = self._store.get_quality_profile(memory_id)

        # Load latest feedback events to derive memory_layer
        events = self._store.get_memory_feedback(memory_id, limit=200)
        layer = self._infer_layer(events)
        last_used_ts = max((e.timestamp for e in events), default=0.0)

        # §5.2 derived metrics
        usage_count   = raw.usage_count
        adoption_rate = raw.adoption_rate
        success_rate  = raw.success_rate
        quality_score = raw.quality_score
        noise_count   = raw.noise_count

        status = self._get_status(raw)

        profile = {
            "memory_id":      memory_id,
            "memory_layer":   layer,
            "usage_count":    usage_count,
            "injection_count": raw.injection_count,
            "adoption_count": raw.adoption_count,
            "success_count":  raw.success_count,
            "noise_count":    noise_count,
            "adoption_rate":  round(adoption_rate, 4),
            "success_rate":   round(success_rate, 4),
            "quality_score":  round(quality_score, 4),
            "last_used":      to_iso(last_used_ts),
            "status":         status,
            "archive_candidate": raw.archive_candidate,
            "build_ms": round((time.time() - t0) * 1000, 1),
        }

        logger.debug("memory_profile: mem=%s quality=%.3f status=%s ms=%.1f",
                     memory_id, quality_score, status, profile["build_ms"])
        return profile

    def build_batch(self, memory_ids: List[str]) -> List[dict]:
        """Build profiles for multiple memories."""
        return [self.build(mid) for mid in memory_ids]

    def build_top(self, n: int = 10, min_usage: int = 1) -> List[dict]:
        """Build profiles for the top-N highest quality memories."""
        profiles = self._store.list_profiles(min_quality=0.0, limit=500)
        eligible = [p for p in profiles if p.usage_count >= min_usage]
        top = sorted(eligible, key=lambda p: p.quality_score, reverse=True)[:n]
        return [self.build(p.memory_id) for p in top]

    def build_noisy(self, min_noise: int = 3) -> List[dict]:
        """Build profiles for high-noise memories (suppression candidates)."""
        noisy = self._store.list_noisy_profiles(min_noise=min_noise)
        return [self.build(p.memory_id) for p in noisy]

    def _infer_layer(self, events) -> str:
        """Infer memory layer from feedback events (most common non-empty value)."""
        layers = [e.memory_layer for e in events if e.memory_layer]
        if not layers:
            return "unknown"
        return max(set(layers), key=layers.count)

    def _get_status(self, profile) -> str:
        """Determine promotion/suppression/normal status."""
        if profile.usage_count < 3:
            return "new"
        if (profile.adoption_rate >= _PROMOTE_MIN_ADOPTION and
                profile.success_rate >= _PROMOTE_MIN_SUCCESS and
                profile.usage_count >= _PROMOTE_MIN_USAGE):
            return "promoted"
        if (profile.noise_count >= _SUPPRESS_MIN_NOISE and
                profile.adoption_rate < _SUPPRESS_MAX_ADOPTION):
            return "suppressed"
        return "normal"
