# 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

"""Dashboard shared helpers — extracted from app.py for Blueprint sharing.

All Blueprint files import from here instead of reaching into app.py globals.
"""

from __future__ import annotations

import collections
import json
import logging
import os
import threading
from datetime import datetime
from pathlib import Path

logger = logging.getLogger(__name__)

# ── Debug mode detection ─────────────────────────────────────────────
_DEBUG = os.environ.get("FAMILIAR_DEBUG", "").lower() in ("1", "true", "yes")


def safe_error(exc: Exception, *, context: str = "") -> str:
    """Return a sanitized error message safe for API responses.

    - Logs full detail server-side (always)
    - Records to analytics store → Activity Errors tab in dashboard
    - In debug mode (FAMILIAR_DEBUG=1), returns the full exception string
    - In production, returns a generic message
    """
    detail = str(exc)
    if context:
        logger.warning("API error in %s: %s", context, detail)
    else:
        logger.warning("API error: %s", detail)

    # Record to analytics store so it appears in Activity > Errors tab
    try:
        from familiar.core.analytics import get_analytics_store
        get_analytics_store().record_error(
            channel="dashboard",
            error_message=f"{context}: {detail}" if context else detail,
            skill=context or "api",
        )
    except Exception:
        pass  # analytics store may not be initialized yet at startup

    if _DEBUG:
        return detail
    return "Internal error — check server logs for details"

# ── Path constants ──────────────────────────────────────────────────────
try:
    from familiar.core.paths import AGENT_DIR, CONFIG_FILE, DATA_DIR
except ImportError:
    AGENT_DIR = Path.home() / ".familiar"
    CONFIG_FILE = AGENT_DIR / "config.yaml"
    DATA_DIR = AGENT_DIR / "data"

TEMPLATES_DIR = Path(__file__).parent / "templates"
STATIC_DIR = Path(__file__).parent / "static"

# ── Global state (set by app.py run_dashboard) ──────────────────────────
_agent = None
_gateway = None
_wizard_host = None
_wizard_lock = threading.Lock()
_marketplace_catalog = None
_service_manager = None
_service_lock = threading.Lock()

# Scheduled message queue — briefings, heartbeats, reminders
scheduled_messages = collections.deque(maxlen=50)

# Persistent latest briefing
_BRIEFING_FILE = DATA_DIR / "latest_briefing.json"


def set_agent(agent) -> None:
    global _agent
    _agent = agent


def set_gateway(gateway) -> None:
    global _gateway
    _gateway = gateway


def get_gateway():
    return _gateway


def get_agent():
    """Get or create the agent singleton."""
    global _agent
    if _agent is None:
        from familiar.core.agent import Agent

        _agent = Agent()
    return _agent


def get_service_manager():
    """Get or create the service manager singleton."""
    global _service_manager
    with _service_lock:
        if _service_manager is None:
            from familiar.services.manager import ServiceManager

            _service_manager = ServiceManager()
    return _service_manager


def get_marketplace_catalog():
    global _marketplace_catalog
    if _marketplace_catalog is None:
        from familiar.marketplace.catalog import MarketplaceCatalog

        _marketplace_catalog = MarketplaceCatalog(auto_seed=True)
    return _marketplace_catalog


def get_message_bridge():
    """Get the message bridge from the agent's gateway, or None."""
    try:
        agent = get_agent()
        gw = getattr(agent, "_gateway", None)
        if gw and hasattr(gw, "message_bridge") and gw.message_bridge:
            return gw.message_bridge
    except Exception:
        pass
    return None


def get_wizard_host():
    """Get or create the connect-wizard adapter for the dashboard."""
    global _wizard_host
    with _wizard_lock:
        if _wizard_host is None:
            from familiar.channels.dashboard_wizard import DashboardWizardHost

            _wizard_host = DashboardWizardHost(get_agent())
    return _wizard_host


# ── Briefing helpers ────────────────────────────────────────────────────


def save_briefing(message: str, source: str = "scheduled", sections: dict = None):
    """Persist the latest briefing to disk."""
    data = {
        "message": message,
        "source": source,
        "timestamp": datetime.now().isoformat(),
    }
    if sections is not None:
        data["sections"] = sections
    try:
        _BRIEFING_FILE.parent.mkdir(parents=True, exist_ok=True)
        _BRIEFING_FILE.write_text(json.dumps(data, indent=2, default=str))
    except OSError as e:
        logger.warning("Failed to save briefing: %s", e)


def load_briefing() -> dict:
    """Load the latest briefing from disk."""
    if _BRIEFING_FILE.exists():
        try:
            return json.loads(_BRIEFING_FILE.read_text())
        except (json.JSONDecodeError, OSError):
            pass
    return {}
