import secrets
import shutil
from pathlib import Path
import threading

# Claude Code conversation directory
CLAUDE_PROJECTS_DIR = Path.home() / ".claude" / "projects"

# Codex conversation directory
CODEX_SESSIONS_DIR = Path.home() / ".codex" / "sessions"

# Plugin path for zellij-send-keys
ZELLIJ_PLUGIN = Path.home() / ".config" / "zellij" / "plugins" / "zellij-send-keys.wasm"


def _resolve_zellij_bin():
    """Resolve zellij binary even when PATH is minimal (eg launchctl)."""
    from_path = shutil.which("zellij")
    if from_path:
        return from_path

    common_paths = (
        "/opt/homebrew/bin/zellij",  # Apple Silicon Homebrew
        "/usr/local/bin/zellij",     # Intel Homebrew
        "/usr/bin/zellij",
    )
    for candidate in common_paths:
        p = Path(candidate)
        if p.exists() and p.is_file():
            return str(p)
    return "zellij"  # Fallback for explicit error reporting by subprocess


ZELLIJ_BIN = _resolve_zellij_bin()

# Runtime data lives in XDG data dir
DATA_DIR = Path.home() / ".local" / "share" / "amc"
SESSIONS_DIR = DATA_DIR / "sessions"
EVENTS_DIR = DATA_DIR / "events"

# Source files live in project directory (relative to this module)
PROJECT_DIR = Path(__file__).resolve().parent.parent
DASHBOARD_DIR = PROJECT_DIR / "dashboard"

PORT = 7400
STALE_EVENT_AGE = 86400  # 24 hours in seconds
STALE_STARTING_AGE = 3600  # 1 hour - sessions stuck in "starting" are orphans
CODEX_ACTIVE_WINDOW = 600  # 10 minutes - only discover recently-active Codex sessions

# Cache for Zellij session list (avoid calling zellij on every request)
_zellij_cache = {"sessions": None, "expires": 0}

# Cache for Codex pane info (avoid running pgrep/ps/lsof on every request)
_codex_pane_cache = {"pid_info": {}, "cwd_map": {}, "expires": 0}

# Cache for parsed context usage by transcript file path + mtime/size
# Limited to prevent unbounded memory growth
_context_usage_cache = {}
_CONTEXT_CACHE_MAX = 100

# Cache mapping Codex session IDs to transcript paths (or None when missing)
_codex_transcript_cache = {}
_CODEX_CACHE_MAX = 200

# Codex sessions dismissed during this server lifetime (prevents re-discovery)
# Uses dict (not set) for O(1) lookup + FIFO eviction via insertion order (Python 3.7+)
_dismissed_codex_ids = {}
_DISMISSED_MAX = 500

# Serialize state collection because it mutates session files/caches.
_state_lock = threading.Lock()

# Projects directory for spawning agents
PROJECTS_DIR = Path.home() / 'projects'

# Default Zellij session for spawning
ZELLIJ_SESSION = 'infra'

# Lock for serializing spawn operations (prevents Zellij race conditions)
_spawn_lock = threading.Lock()

# Rate limiting: track last spawn time per project (prevents spam)
_spawn_timestamps: dict[str, float] = {}
SPAWN_COOLDOWN_SEC = 10.0

# Auth token for spawn endpoint
_auth_token: str = ''


def generate_auth_token():
    """Generate a one-time auth token for this server instance."""
    global _auth_token
    _auth_token = secrets.token_urlsafe(32)
    return _auth_token


def validate_auth_token(request_token: str) -> bool:
    """Validate the Authorization header token."""
    return request_token == f'Bearer {_auth_token}'


def start_projects_watcher():
    """Start background thread to refresh projects cache every 5 minutes."""
    import logging
    from amc_server.mixins.spawn import load_projects_cache

    def _watch_loop():
        import time
        while True:
            try:
                time.sleep(300)
                load_projects_cache()
            except Exception:
                logging.exception('Projects cache refresh failed')

    thread = threading.Thread(target=_watch_loop, daemon=True)
    thread.start()
