# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# Licensed under the MIT License

"""
Email Engagement Store — tracks per-sender interaction history.

Records opens, replies, trashes, and ignores per sender email address.
Engagement scores are used by the unified scorer (Sprint 2) to weight
classification — senders you interact with rank higher than senders
you ignore.

State persisted at: ~/.familiar/data/email_engagement.json
"""

import json
import logging
import threading
from datetime import date, datetime
from pathlib import Path

logger = logging.getLogger(__name__)

_lock = threading.Lock()


def _data_dir() -> Path:
    try:
        from familiar.core.paths import DATA_DIR
        return DATA_DIR
    except ImportError:
        return Path.home() / ".familiar" / "data"


def _engagement_path() -> Path:
    return _data_dir() / "email_engagement.json"


def _load() -> dict:
    path = _engagement_path()
    if path.exists():
        try:
            with open(path) as f:
                return json.load(f)
        except (json.JSONDecodeError, OSError) as e:
            logger.warning("Failed to load engagement data: %s", e)
    return {"senders": {}, "version": 1}


def _save(data: dict):
    path = _engagement_path()
    with _lock:
        path.parent.mkdir(parents=True, exist_ok=True)
        tmp = path.with_suffix(".tmp")
        tmp.write_text(json.dumps(data, indent=2, default=str))
        tmp.replace(path)


def _ensure_sender(data: dict, sender: str) -> dict:
    """Get or create a sender record."""
    sender = sender.lower().strip()
    if not sender:
        return {}
    if sender not in data["senders"]:
        data["senders"][sender] = {
            "total_received": 0,
            "opened": 0,
            "replied": 0,
            "trashed": 0,
            "last_interaction": None,
            "first_seen": date.today().isoformat(),
            "engagement_score": 0.0,
        }
    return data["senders"][sender]


def _recalc_score(record: dict) -> float:
    """Calculate engagement score.

    Formula: (replies × 10 + opens × 2 + 1) / (total_received + 1)
    High (>2.0): important — user actively communicates
    Medium (0.5-2.0): notifications worth seeing
    Low (<0.5): promotional/ignorable — user ignores this sender
    """
    replies = record.get("replied", 0)
    opens = record.get("opened", 0)
    total = record.get("total_received", 0)
    return round((replies * 10 + opens * 2 + 1) / (total + 1), 3)


# ── Public API ────────────────────────────────────────────────────────────────


def record_received(sender: str):
    """Record that an email was received from this sender."""
    data = _load()
    rec = _ensure_sender(data, sender)
    if not rec:
        return
    rec["total_received"] = rec.get("total_received", 0) + 1
    rec["engagement_score"] = _recalc_score(rec)
    _save(data)


def record_opened(sender: str):
    """Record that the user opened/read an email from this sender."""
    data = _load()
    rec = _ensure_sender(data, sender)
    if not rec:
        return
    rec["opened"] = rec.get("opened", 0) + 1
    rec["last_interaction"] = datetime.now().isoformat()
    rec["engagement_score"] = _recalc_score(rec)
    _save(data)


def record_replied(sender: str):
    """Record that the user replied to an email from this sender."""
    data = _load()
    rec = _ensure_sender(data, sender)
    if not rec:
        return
    rec["replied"] = rec.get("replied", 0) + 1
    rec["last_interaction"] = datetime.now().isoformat()
    rec["engagement_score"] = _recalc_score(rec)
    _save(data)


def record_trashed(sender: str):
    """Record that the user trashed an email from this sender."""
    data = _load()
    rec = _ensure_sender(data, sender)
    if not rec:
        return
    rec["trashed"] = rec.get("trashed", 0) + 1
    rec["last_interaction"] = datetime.now().isoformat()
    rec["engagement_score"] = _recalc_score(rec)
    _save(data)


def get_engagement(sender: str) -> dict:
    """Get engagement data for a specific sender."""
    data = _load()
    return data["senders"].get(sender.lower().strip(), {})


def get_score(sender: str) -> float:
    """Get engagement score for a sender. Returns 0.0 if unknown."""
    rec = get_engagement(sender)
    return rec.get("engagement_score", 0.0)


def get_top_engaged(limit: int = 20) -> list:
    """Get senders with highest engagement scores."""
    data = _load()
    senders = [
        {"sender": k, **v}
        for k, v in data["senders"].items()
        if v.get("engagement_score", 0) > 0
    ]
    senders.sort(key=lambda s: s["engagement_score"], reverse=True)
    return senders[:limit]


def get_low_engaged(min_received: int = 5, limit: int = 20) -> list:
    """Get senders with low engagement (received many, never opened)."""
    data = _load()
    senders = [
        {"sender": k, **v}
        for k, v in data["senders"].items()
        if v.get("total_received", 0) >= min_received
        and v.get("engagement_score", 0) < 0.3
    ]
    senders.sort(key=lambda s: s["total_received"], reverse=True)
    return senders[:limit]


def get_stats() -> dict:
    """Get overall engagement statistics."""
    data = _load()
    senders = data.get("senders", {})
    total = len(senders)
    if not total:
        return {"total_senders": 0, "avg_score": 0, "high": 0, "medium": 0, "low": 0}

    scores = [v.get("engagement_score", 0) for v in senders.values()]
    return {
        "total_senders": total,
        "avg_score": round(sum(scores) / total, 3),
        "high": sum(1 for s in scores if s > 2.0),
        "medium": sum(1 for s in scores if 0.5 <= s <= 2.0),
        "low": sum(1 for s in scores if s < 0.5),
    }
