"""Agent configuration via environment variables."""

import socket
from functools import lru_cache
from typing import Optional

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict

from app import __version__


class Settings(BaseSettings):
    """Agent settings loaded from environment variables.

    All settings are frozen after initialization to prevent runtime mutation.
    """

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        frozen=True,  # Prevent runtime mutation
    )

    # Agent identity
    agent_version: str = Field(default=__version__, description="Agent version for API headers")
    hostname: str = Field(default_factory=socket.gethostname, description="Agent hostname")
    agent_id: Optional[str] = Field(default=None, description="Assigned by orchestrator after registration")

    # ============================================================
    # LOCAL_MODE SETTINGS (Phase 2)
    # When LOCAL_MODE=true, agent uses /register-local endpoint
    # with LOCAL_AGENT_KEY for registration (no registration token needed)
    # ============================================================
    local_mode: bool = Field(
        default=False,
        description="Enable LOCAL_MODE for single-tenant registration via LOCAL_AGENT_KEY"
    )
    local_agent_key: Optional[str] = Field(
        default=None,
        description="Key for local registration. Required when LOCAL_MODE=true."
    )

    # New secure registration (recommended for multi-tenant)
    registration_token: Optional[str] = Field(
        default=None,
        description="Registration token from orchestrator. Required for first-time registration (multi-tenant)."
    )

    # Agent credentials (set after registration, persisted to disk)
    agent_secret: Optional[str] = Field(
        default=None,
        description="Agent secret for authentication. Set after registration."
    )

    # Tenant assignment (derived from registration token, or can be set directly for legacy)
    tenant_id: Optional[str] = Field(
        default=None,
        description="Tenant UUID this agent belongs to. Set after registration."
    )

    # Orchestrator connection
    # Default URL is for Docker-based dev where orchestrator runs on the host.
    # When running directly on a Linux tenant server, set ORCHESTRATOR_URL to
    # the orchestrator's public URL (e.g., "https://orchestrator.letsbe.io").
    orchestrator_url: str = Field(
        default="http://host.docker.internal:8000",
        description="Orchestrator API base URL"
    )

    # Hub connection (for direct credential sync)
    # When HUB_URL and HUB_API_KEY are set, agent sends heartbeats with
    # credentials directly to the Hub (bypassing orchestrator for this purpose)
    hub_url: Optional[str] = Field(
        default=None,
        description="Hub API base URL for credential sync (e.g., https://hub.letsbe.io)"
    )
    hub_api_key: Optional[str] = Field(
        default=None,
        description="Hub API key for authentication (from ServerConnection.hubApiKey)"
    )
    hub_telemetry_enabled: bool = Field(
        default=True,
        description="Enable sending heartbeats with credentials to Hub"
    )

    # Legacy auth (deprecated - use registration_token + agent_secret instead)
    agent_token: Optional[str] = Field(
        default=None,
        description="[DEPRECATED] Legacy authentication token. Use agent_secret instead."
    )

    # Timing intervals (seconds)
    heartbeat_interval: int = Field(default=30, ge=5, le=300, description="Heartbeat interval")
    poll_interval: int = Field(default=5, ge=1, le=60, description="Task polling interval")

    # Logging
    log_level: str = Field(default="INFO", description="Log level (DEBUG, INFO, WARNING, ERROR)")
    log_json: bool = Field(default=True, description="Output logs as JSON")

    # Resilience
    max_concurrent_tasks: int = Field(default=3, ge=1, le=10, description="Max concurrent task executions")
    backoff_base: float = Field(default=1.0, ge=0.1, le=10.0, description="Base backoff time in seconds")
    backoff_max: float = Field(default=60.0, ge=10.0, le=300.0, description="Max backoff time in seconds")
    circuit_breaker_threshold: int = Field(default=5, ge=1, le=20, description="Consecutive failures to trip breaker")
    circuit_breaker_cooldown: int = Field(default=30, ge=10, le=900, description="Cooldown period in seconds")

    # Security - File operations
    allowed_file_root: str = Field(default="/opt/letsbe", description="Root directory for file operations")
    allowed_env_root: str = Field(default="/opt/letsbe/env", description="Root directory for ENV file operations")
    max_file_size: int = Field(default=10 * 1024 * 1024, description="Max file size in bytes (default 10MB)")

    # Security - Shell operations
    shell_timeout: int = Field(default=60, ge=5, le=600, description="Default shell command timeout")

    # Security - Docker operations
    allowed_compose_paths: list[str] = Field(
        default=["/opt/letsbe", "/home/letsbe"],
        description="Allowed directories for compose files"
    )
    allowed_stacks_root: str = Field(
        default="/opt/letsbe/stacks",
        description="Root directory for Docker stack operations"
    )

    # Local persistence
    pending_results_path: str = Field(
        default="~/.letsbe-agent/pending_results.json",
        description="Path for buffering unsent task results"
    )
    credentials_path: str = Field(
        default="~/.letsbe-agent/credentials.json",
        description="Path for persisting agent credentials after registration"
    )

    # Playwright browser automation
    playwright_artifacts_dir: str = Field(
        default="/opt/letsbe/playwright-artifacts",
        description="Directory for screenshots, traces, and other browser artifacts"
    )
    playwright_default_timeout_ms: int = Field(
        default=60000, ge=5000, le=300000,
        description="Default timeout for Playwright actions in milliseconds"
    )
    playwright_navigation_timeout_ms: int = Field(
        default=120000, ge=10000, le=300000,
        description="Timeout for page navigation in milliseconds"
    )
    mcp_service_url: Optional[str] = Field(
        default=None,
        description="URL for Playwright MCP sidecar service (for exploratory mode)"
    )


@lru_cache
def get_settings() -> Settings:
    """Get cached settings instance.

    Settings are loaded once and cached for the lifetime of the process.
    """
    return Settings()
