# 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

"""
Email Action Queue — tracks emails requiring follow-up action.

After triage, emails that need replies, acknowledgments, RSVP responses,
or delegation are queued here. The proactive system surfaces them in
heartbeat check-ins and daily briefings.

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

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

logger = logging.getLogger(__name__)

_lock = threading.Lock()


# Action types — what the user needs to do with this email
ACTION_TYPES = {
    "thank_you": "Send thank-you / acknowledgment",
    "rsvp": "RSVP to invitation",
    "info_request": "Reply with requested information",
    "deadline": "Track deadline from this email",
    "delegate": "Forward / delegate to staff member",
    "draft_acknowledgment": "Draft formal gift acknowledgment letter",
    "board_prep": "Prepare for referenced board meeting",
    "grant_report": "Track grant report deadline",
    "legislative_action": "Review advocacy action item",
}


def _data_dir() -> Path:
    try:
        from familiar.core.paths import DATA_DIR

        return DATA_DIR
    except ImportError:
        return Path.home() / ".familiar" / "data"


def _store_path() -> Path:
    return _data_dir() / "email_actions.json"


class EmailActionStore:
    """Thread-safe store for email follow-up actions.

    Actions flow: monitor/triage detects → store queues → heartbeat
    presents → user acts → mark complete.
    """

    def __init__(self):
        self._path = _store_path()
        self._data: dict = {"actions": []}
        self.load()

    def load(self):
        """Load action queue from disk."""
        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 email actions: %s", e)
                    self._data = {"actions": []}
            else:
                self._data = {"actions": []}

    def save(self):
        """Write action queue to disk atomically."""
        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 add_action(
        self,
        email_id: str,
        email_subject: str,
        email_from: str,
        action_type: str,
        suggested_response: str = "",
        deadline: Optional[str] = None,
        category: str = "",
        priority: str = "action",
    ) -> Optional[str]:
        """Add an action. Returns action ID, or None if duplicate."""
        # Deduplicate by email_id + action_type
        for a in self._data["actions"]:
            if a["email_id"] == email_id and a["action_type"] == action_type:
                return None

        action_id = f"ea_{secrets.token_hex(6)}"
        now = datetime.now(timezone.utc).isoformat()

        self._data["actions"].append(
            {
                "id": action_id,
                "email_id": email_id,
                "email_subject": email_subject,
                "email_from": email_from,
                "action_type": action_type,
                "suggested_response": suggested_response,
                "deadline": deadline,
                "delegate_to": None,
                "status": "pending",
                "detected_at": now,
                "completed_at": None,
                "category": category,
                "priority": priority,
            }
        )
        self.save()
        return action_id

    def get_pending(self) -> list[dict]:
        """Return all pending actions, sorted by priority then date."""
        self.load()
        pending = [a for a in self._data["actions"] if a["status"] == "pending"]
        priority_order = {"urgent": 0, "action": 1, "fyi": 2}
        pending.sort(
            key=lambda a: (
                priority_order.get(a.get("priority", "fyi"), 2),
                a.get("detected_at", ""),
            )
        )
        return pending

    def get_by_type(self, action_type: str) -> list[dict]:
        """Get pending actions of a specific type."""
        return [a for a in self.get_pending() if a["action_type"] == action_type]

    def get_by_index(self, index: int) -> Optional[dict]:
        """Get a pending action by 1-based index."""
        pending = self.get_pending()
        if 1 <= index <= len(pending):
            return pending[index - 1]
        return None

    def mark_complete(self, action_id: str) -> bool:
        """Mark action as completed."""
        for a in self._data["actions"]:
            if a["id"] == action_id:
                a["status"] = "completed"
                a["completed_at"] = datetime.now(timezone.utc).isoformat()
                self.save()
                return True
        return False

    def mark_delegated(self, action_id: str, delegate_to: str) -> bool:
        """Mark action as delegated to a staff member."""
        for a in self._data["actions"]:
            if a["id"] == action_id:
                a["status"] = "delegated"
                a["delegate_to"] = delegate_to
                a["completed_at"] = datetime.now(timezone.utc).isoformat()
                self.save()
                return True
        return False

    def cleanup_stale(self, max_age_days: int = 14):
        """Remove completed/delegated actions older than max_age_days."""
        now = datetime.now(timezone.utc)
        keep = []
        for a in self._data["actions"]:
            if a["status"] == "pending":
                keep.append(a)
                continue
            detected = a.get("detected_at", "")
            if not detected:
                continue
            try:
                dt = datetime.fromisoformat(detected.replace("Z", "+00:00"))
                if (now - dt).days <= max_age_days:
                    keep.append(a)
            except ValueError:
                keep.append(a)
        if len(keep) != len(self._data["actions"]):
            self._data["actions"] = keep
            self.save()

    def count_pending(self) -> int:
        """Quick count of pending actions."""
        self.load()
        return sum(1 for a in self._data["actions"] if a["status"] == "pending")

    def summary_by_type(self) -> dict[str, int]:
        """Count pending actions grouped by type."""
        counts: dict[str, int] = {}
        for a in self.get_pending():
            t = a["action_type"]
            counts[t] = counts.get(t, 0) + 1
        return counts


def get_action_store() -> EmailActionStore:
    """Get the email action store."""
    return EmailActionStore()
