"""
Configuration module for AMI SDK.

Handles environment variable loading, configuration precedence,
and context resolution for API requests.

Precedence (highest to lowest):
1. Method arguments (per-call)
2. Constructor arguments
3. Environment variables
4. SDK defaults
"""

import os
from dataclasses import dataclass
from typing import Optional, Dict, Any


class ConfigError(Exception):
    """Raised when configuration is invalid or incomplete."""
    pass


@dataclass(frozen=True)
class AMIConfig:
    """
    Immutable configuration for AMI SDK client.

    Args:
        api_key: AMI API key (required). Falls back to AMI_API_KEY env var.
        base_url: API base URL. Defaults to https://api.memrail.com
        org: Organization slug. Falls back to AMI_ORG env var.
        team: Team slug. Falls back to AMI_TEAM env var.
        workspace: Workspace slug. Falls back to AMI_WORKSPACE env var.
        project: Project slug. Falls back to AMI_PROJECT env var.
        org_id: Organization ID (overrides org slug). Falls back to AMI_ORG_ID.
        team_id: Team ID (overrides team slug). Falls back to AMI_TEAM_ID.
        workspace_id: Workspace ID (overrides workspace slug). Falls back to AMI_WORKSPACE_ID.
        project_id: Project ID (overrides project slug). Falls back to AMI_PROJECT_ID.
        timeout_s: HTTP request timeout in seconds. Default: 10.0
        max_retries: Maximum number of retries for failed requests. Default: 3
        use_env: Whether to load values from environment variables. Default: True

    Raises:
        ConfigError: If required configuration (api_key) is missing.
    """

    api_key: Optional[str] = None
    base_url: str = "https://api.memrail.com"
    org: Optional[str] = None
    team: Optional[str] = None
    workspace: Optional[str] = None
    project: Optional[str] = None
    org_id: Optional[str] = None
    team_id: Optional[str] = None
    workspace_id: Optional[str] = None
    project_id: Optional[str] = None
    timeout_s: float = 10.0
    max_retries: int = 3
    use_env: bool = True

    def __post_init__(self):
        """Validate configuration and normalize values after initialization."""
        # Normalize base URL (remove trailing slash)
        if self.base_url and self.base_url.endswith("/"):
            object.__setattr__(self, "base_url", self.base_url.rstrip("/"))

        # Validate required fields
        if not self.api_key:
            raise ConfigError(
                "AMI_API_KEY is required. Provide via api_key parameter or AMI_API_KEY environment variable."
            )

    @staticmethod
    def create(
        api_key: Optional[str] = None,
        base_url: Optional[str] = None,
        org: Optional[str] = None,
        team: Optional[str] = None,
        workspace: Optional[str] = None,
        project: Optional[str] = None,
        org_id: Optional[str] = None,
        team_id: Optional[str] = None,
        workspace_id: Optional[str] = None,
        project_id: Optional[str] = None,
        timeout_s: Optional[float] = None,
        max_retries: Optional[int] = None,
        use_env: bool = True,
    ) -> "AMIConfig":
        """
        Factory method to create config with environment variable fallback.

        Args are the same as AMIConfig dataclass fields.

        Returns:
            AMIConfig instance with proper precedence applied
        """
        # Build kwargs with precedence: explicit args > env > defaults
        kwargs = {}

        if use_env:
            # Load from environment
            env_values = {
                "api_key": os.getenv("AMI_API_KEY"),
                "base_url": os.getenv("AMI_BASE_URL"),
                "org": os.getenv("AMI_ORG"),
                "team": os.getenv("AMI_TEAM"),
                "workspace": os.getenv("AMI_WORKSPACE"),
                "project": os.getenv("AMI_PROJECT"),
                "org_id": os.getenv("AMI_ORG_ID"),
                "team_id": os.getenv("AMI_TEAM_ID"),
                "workspace_id": os.getenv("AMI_WORKSPACE_ID"),
                "project_id": os.getenv("AMI_PROJECT_ID"),
            }

            # Start with env values (non-None only)
            kwargs = {k: v for k, v in env_values.items() if v is not None}

        # Override with explicit arguments (non-None only)
        explicit_args = {
            "api_key": api_key,
            "base_url": base_url,
            "org": org,
            "team": team,
            "workspace": workspace,
            "project": project,
            "org_id": org_id,
            "team_id": team_id,
            "workspace_id": workspace_id,
            "project_id": project_id,
            "timeout_s": timeout_s,
            "max_retries": max_retries,
        }
        kwargs.update({k: v for k, v in explicit_args.items() if v is not None})

        # Set defaults for timeout and retries if not provided
        if "timeout_s" not in kwargs:
            kwargs["timeout_s"] = 10.0
        if "max_retries" not in kwargs:
            kwargs["max_retries"] = 3
        if "base_url" not in kwargs:
            kwargs["base_url"] = "https://api.memrail.com"

        kwargs["use_env"] = use_env

        return AMIConfig(**kwargs)

    def resolve_context(
        self,
        workspace: Optional[str] = None,
        project: Optional[str] = None,
        workspace_id: Optional[str] = None,
        project_id: Optional[str] = None,
        org: Optional[str] = None,
        team: Optional[str] = None,
        org_id: Optional[str] = None,
        team_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Resolve context for API request with method args taking precedence.

        Args:
            workspace: Workspace slug (overrides config)
            project: Project slug (overrides config)
            workspace_id: Workspace ID (overrides workspace slug)
            project_id: Project ID (overrides project slug)
            org: Organization slug (overrides config)
            team: Team slug (overrides config)
            org_id: Organization ID (overrides org slug)
            team_id: Team ID (overrides team slug)

        Returns:
            Dictionary with resolved context values
        """
        context = {}

        # Resolve org (method arg > config, ID > slug)
        resolved_org = org if org is not None else self.org
        resolved_org_id = org_id if org_id is not None else self.org_id
        if resolved_org_id:
            context["org_id"] = resolved_org_id
        elif resolved_org:
            context["org"] = resolved_org

        # Resolve team (method arg > config, ID > slug)
        resolved_team = team if team is not None else self.team
        resolved_team_id = team_id if team_id is not None else self.team_id
        if resolved_team_id:
            context["team_id"] = resolved_team_id
        elif resolved_team:
            context["team"] = resolved_team

        # Resolve workspace (method arg > config, ID > slug)
        resolved_workspace = workspace if workspace is not None else self.workspace
        resolved_workspace_id = workspace_id if workspace_id is not None else self.workspace_id
        if resolved_workspace_id:
            context["workspace_id"] = resolved_workspace_id
        elif resolved_workspace:
            context["workspace"] = resolved_workspace

        # Resolve project (method arg > config, ID > slug)
        resolved_project = project if project is not None else self.project
        resolved_project_id = project_id if project_id is not None else self.project_id
        if resolved_project_id:
            context["project_id"] = resolved_project_id
        elif resolved_project:
            context["project"] = resolved_project

        return context

    def require_workspace(
        self,
        workspace: Optional[str] = None,
        workspace_id: Optional[str] = None,
    ) -> str:
        """
        Ensure workspace is available, raising error if not.

        Args:
            workspace: Workspace slug (overrides config)
            workspace_id: Workspace ID (overrides config)

        Returns:
            Resolved workspace identifier (slug or ID)

        Raises:
            ConfigError: If workspace cannot be resolved
        """
        resolved_workspace_id = workspace_id or self.workspace_id
        resolved_workspace = workspace or self.workspace

        if not resolved_workspace_id and not resolved_workspace:
            raise ConfigError(
                "workspace is required but not provided. "
                "Set via constructor, environment (AMI_WORKSPACE/AMI_WORKSPACE_ID), or method argument."
            )

        return resolved_workspace_id or resolved_workspace

    def require_project(
        self,
        project: Optional[str] = None,
        project_id: Optional[str] = None,
    ) -> str:
        """
        Ensure project is available, raising error if not.

        Args:
            project: Project slug (overrides config)
            project_id: Project ID (overrides config)

        Returns:
            Resolved project identifier (slug or ID)

        Raises:
            ConfigError: If project cannot be resolved
        """
        resolved_project_id = project_id or self.project_id
        resolved_project = project or self.project

        if not resolved_project_id and not resolved_project:
            raise ConfigError(
                "project is required but not provided. "
                "Set via constructor, environment (AMI_PROJECT/AMI_PROJECT_ID), or method argument."
            )

        return resolved_project_id or resolved_project
