# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Stewardship Engine — automated donor follow-up with configurable timelines.

48-hour thank-you window increases repeat giving 40% (AFP benchmarks).
Auto-creates stewardship tasks when gifts are detected from email triage
or manual donor logging.

Stored at: ~/.familiar/data/stewardship.json
"""

import json
import logging
import os
import secrets
import threading
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Optional

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"


# ── Stewardship Matrix ────────────────────────────────────────────────
# Maps gift size ranges to required actions with due-day offsets.
# Format: (action_type, days_after_trigger)

STEWARDSHIP_MATRIX = {
    "any": [
        ("thank_you_email", 2),
    ],
    "100+": [
        ("thank_you_email", 1),
        ("phone_call", 7),
    ],
    "1000+": [
        ("thank_you_email", 1),
        ("thank_you_letter", 2),
        ("phone_call", 3),
    ],
    "10000+": [
        ("thank_you_email", 1),
        ("thank_you_letter", 2),
        ("phone_call", 3),
        ("recognition", 14),
        ("impact_report", 30),
    ],
    "first_time": [
        ("welcome_packet", 7),
    ],
}

ACTION_LABELS = {
    "thank_you_email": "Send thank-you email",
    "thank_you_letter": "Mail formal thank-you letter",
    "phone_call": "Personal phone call",
    "welcome_packet": "Send welcome / new donor packet",
    "recognition": "Public recognition / social media mention",
    "impact_report": "Send personalized impact report",
}


def _get_actions_for_gift(amount: float, is_first_time: bool = False) -> list[tuple[str, int]]:
    """Determine stewardship actions based on gift amount and donor status."""
    actions = []
    if amount >= 10000:
        actions = list(STEWARDSHIP_MATRIX["10000+"])
    elif amount >= 1000:
        actions = list(STEWARDSHIP_MATRIX["1000+"])
    elif amount >= 100:
        actions = list(STEWARDSHIP_MATRIX["100+"])
    else:
        actions = list(STEWARDSHIP_MATRIX["any"])

    if is_first_time:
        actions.extend(STEWARDSHIP_MATRIX["first_time"])

    return actions


# ── StewardshipStore ──────────────────────────────────────────────────


class StewardshipStore:
    """Thread-safe store for donor stewardship tracking."""

    def __init__(self):
        self._path = _data_dir() / "stewardship.json"
        self._data: dict = {"entries": []}
        self.load()

    def load(self):
        with _lock:
            if self._path.exists():
                try:
                    self._data = json.loads(self._path.read_text())
                except (json.JSONDecodeError, OSError) as e:
                    logger.warning("Failed to load stewardship data: %s", e)
                    self._data = {"entries": []}
            else:
                self._data = {"entries": []}

    def save(self):
        with _lock:
            self._path.parent.mkdir(parents=True, exist_ok=True)
            tmp = self._path.with_suffix(".tmp")
            tmp.write_text(json.dumps(self._data, indent=2))
            tmp.replace(self._path)
            try:
                os.chmod(self._path, 0o600)
            except OSError:
                pass

    def create_stewardship(
        self,
        donor_id: str,
        donor_name: str,
        donor_email: str = "",
        trigger_type: str = "gift_detected",
        trigger_amount: float = 0,
        is_first_time: bool = False,
    ) -> Optional[str]:
        """Create stewardship entry with auto-generated actions. Returns ID or None if dupe."""
        # Prevent duplicate stewardship for same donor + same day
        today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
        for e in self._data["entries"]:
            if (
                e["donor_id"] == donor_id
                and e["trigger_type"] == trigger_type
                and e["created_at"][:10] == today
            ):
                return None

        entry_id = f"stw_{secrets.token_hex(6)}"
        now = datetime.now(timezone.utc)
        now_iso = now.isoformat()

        actions_spec = _get_actions_for_gift(trigger_amount, is_first_time)
        actions = []
        for action_type, days_after in actions_spec:
            due = now + timedelta(days=days_after)
            actions.append(
                {
                    "type": action_type,
                    "label": ACTION_LABELS.get(action_type, action_type),
                    "due_date": due.strftime("%Y-%m-%d"),
                    "status": "pending",
                    "completed_at": None,
                    "notes": "",
                }
            )

        self._data["entries"].append(
            {
                "id": entry_id,
                "donor_id": donor_id,
                "donor_name": donor_name,
                "donor_email": donor_email,
                "trigger_type": trigger_type,
                "trigger_amount": trigger_amount,
                "trigger_date": today,
                "actions": actions,
                "created_at": now_iso,
            }
        )
        self.save()
        return entry_id

    def get_entry(self, entry_id: str) -> Optional[dict]:
        self.load()
        for e in self._data["entries"]:
            if e["id"] == entry_id:
                return e
        return None

    def get_overdue(self) -> list[dict]:
        """Get entries with overdue actions."""
        self.load()
        today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
        results = []
        for e in self._data["entries"]:
            overdue_actions = [
                a for a in e["actions"] if a["status"] == "pending" and a["due_date"] < today
            ]
            if overdue_actions:
                results.append(
                    {
                        "entry_id": e["id"],
                        "donor_name": e["donor_name"],
                        "donor_email": e.get("donor_email", ""),
                        "trigger_amount": e["trigger_amount"],
                        "overdue_actions": overdue_actions,
                    }
                )
        return results

    def get_pending(self, days: int = 7) -> list[dict]:
        """Get entries with actions due within N days."""
        self.load()
        today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
        cutoff = (datetime.now(timezone.utc) + timedelta(days=days)).strftime("%Y-%m-%d")
        results = []
        for e in self._data["entries"]:
            pending_actions = [
                a
                for a in e["actions"]
                if a["status"] == "pending" and today <= a["due_date"] <= cutoff
            ]
            if pending_actions:
                results.append(
                    {
                        "entry_id": e["id"],
                        "donor_name": e["donor_name"],
                        "donor_email": e.get("donor_email", ""),
                        "trigger_amount": e["trigger_amount"],
                        "pending_actions": pending_actions,
                    }
                )
        return results

    def complete_action(self, entry_id: str, action_index: int) -> bool:
        """Mark a stewardship action as completed."""
        for e in self._data["entries"]:
            if e["id"] == entry_id:
                if 0 <= action_index < len(e["actions"]):
                    e["actions"][action_index]["status"] = "completed"
                    e["actions"][action_index]["completed_at"] = datetime.now(
                        timezone.utc
                    ).isoformat()
                    self.save()
                    return True
        return False

    def skip_action(self, entry_id: str, action_index: int) -> bool:
        """Skip a stewardship action."""
        for e in self._data["entries"]:
            if e["id"] == entry_id:
                if 0 <= action_index < len(e["actions"]):
                    e["actions"][action_index]["status"] = "skipped"
                    self.save()
                    return True
        return False

    def get_for_donor(self, donor_id: str) -> list[dict]:
        """Get stewardship history for a donor."""
        self.load()
        return [e for e in self._data["entries"] if e["donor_id"] == donor_id]

    def count_overdue(self) -> int:
        return len(self.get_overdue())

    def count_pending(self) -> int:
        return len(self.get_pending())

    def completion_rate(self) -> float:
        """Compute completion rate across all actions."""
        self.load()
        total = 0
        completed = 0
        for e in self._data["entries"]:
            for a in e["actions"]:
                total += 1
                if a["status"] == "completed":
                    completed += 1
        return round(completed / total * 100, 1) if total else 0.0


def get_stewardship_store() -> StewardshipStore:
    return StewardshipStore()
