import json
import threading
from datetime import datetime, timezone
from pathlib import Path

from pulse.core.paths import get_state_dir
from pulse.core import event_bus

YIELD_FILE = get_state_dir() / "pattern_yields.json"
MAX_PATTERNS = 1000

_lock = threading.RLock()
_registry = {}

def _init_entry(pattern_id: str) -> dict:
    return {
        "pattern_id": pattern_id,
        "match_count": 0,
        "useful_count": 0,
        "yield_rate": 0.0,
        "last_matched": None,
        "deprecated": False
    }

def _load_registry():
    global _registry
    with _lock:
        if not YIELD_FILE.exists():
            _registry = {}
            return
        try:
            with open(YIELD_FILE, "r", encoding="utf-8") as f:
                _registry = json.load(f)
        except Exception:
            _registry = {}

def _save_registry():
    with _lock:
        _prune_if_needed()
        YIELD_FILE.parent.mkdir(parents=True, exist_ok=True)
        with open(YIELD_FILE, "w", encoding="utf-8") as f:
            json.dump(_registry, f, indent=2)

def _prune_if_needed():
    global _registry
    if len(_registry) > MAX_PATTERNS:
        # Sort by: deprecated first (True sorts before False if we use 'not x.get("deprecated", False)')
        # So deprecated patterns are at the beginning of the list.
        # Then by match_count ascending (so lowest match counts are at the beginning).
        # We want to discard the beginning of the list and keep the end.
        sorted_patterns = sorted(
            _registry.values(),
            key=lambda x: (not x.get("deprecated", False), x.get("match_count", 0))
        )
        # Keep top MAX_PATTERNS (the last MAX_PATTERNS elements)
        keep = sorted_patterns[-MAX_PATTERNS:]
        _registry = {p["pattern_id"]: p for p in keep}

def record_match(pattern_id: str):
    with _lock:
        if not _registry:
            _load_registry()
        if pattern_id not in _registry:
            _registry[pattern_id] = _init_entry(pattern_id)
        entry = _registry[pattern_id]
        entry["match_count"] += 1
        entry["last_matched"] = datetime.now(timezone.utc).isoformat()
        entry["yield_rate"] = entry["useful_count"] / entry["match_count"]
        _save_registry()

def record_useful(pattern_id: str):
    with _lock:
        if not _registry:
            _load_registry()
        if pattern_id not in _registry:
            _registry[pattern_id] = _init_entry(pattern_id)
        entry = _registry[pattern_id]
        entry["useful_count"] += 1
        if entry["match_count"] < entry["useful_count"]:
            entry["match_count"] = entry["useful_count"]  # Sanity check
        entry["yield_rate"] = entry["useful_count"] / entry["match_count"]
        _save_registry()

def get_yield_rate(pattern_id: str) -> float:
    with _lock:
        if not _registry:
            _load_registry()
        entry = _registry.get(pattern_id)
        return entry["yield_rate"] if entry else 0.0

def get_underperformers(threshold: float = 0.1) -> list:
    with _lock:
        if not _registry:
            _load_registry()
        return [
            p["pattern_id"] for p in _registry.values()
            if p["match_count"] > 20 and p["yield_rate"] < threshold and not p.get("deprecated", False)
        ]

def deprecate(pattern_id: str):
    with _lock:
        if not _registry:
            _load_registry()
        if pattern_id not in _registry:
            _registry[pattern_id] = _init_entry(pattern_id)
        entry = _registry[pattern_id]
        if not entry.get("deprecated"):
            entry["deprecated"] = True
            _save_registry()
            event_bus.publish("pattern.deprecated", "pattern_yield", {
                "pattern_id": pattern_id,
                "match_count": entry["match_count"],
                "yield_rate": entry["yield_rate"]
            })
