"""Tenant management service for multi-tenant CLI operations."""

from __future__ import annotations

import time
from typing import TYPE_CHECKING

import httpx

from teams_phone.constants import GRAPH_API_V1_BASE
from teams_phone.exceptions import ConfigurationError
from teams_phone.models import ConnectionTestResult, TenantProfile

if TYPE_CHECKING:
    from teams_phone.infrastructure import ConfigManager
    from teams_phone.services import AuthService


class TenantService:
    """Service for managing tenant profiles.

    Provides CRUD operations for tenant profiles, delegating persistence
    to ConfigManager. Coordinates with AuthService to check authentication
    status when removing tenants.

    Attributes:
        config_manager: Manager for tenant configuration persistence.
        auth_service: Service for checking authentication status.
    """

    def __init__(
        self, config_manager: ConfigManager, auth_service: AuthService
    ) -> None:
        """Initialize the TenantService.

        Args:
            config_manager: ConfigManager instance for tenant persistence.
            auth_service: AuthService instance for authentication checks.
        """
        self.config_manager = config_manager
        self.auth_service = auth_service

    def list_tenants(self) -> list[TenantProfile]:
        """List all configured tenant profiles.

        Returns:
            List of TenantProfile objects, sorted alphabetically by name.
        """
        config = self.config_manager.load_config()
        return sorted(config.tenants.values(), key=lambda t: t.name)

    def get_current_tenant(self) -> TenantProfile:
        """Get the current default tenant profile.

        Returns:
            The TenantProfile configured as the default tenant.

        Raises:
            ConfigurationError: If no default tenant is configured.
        """
        default_name = self.config_manager.get_default_tenant()
        if default_name is None:
            raise ConfigurationError(
                "No default tenant configured",
                remediation=(
                    "Set a default tenant with 'teams-phone tenants switch <name>'.\n"
                    "Or add a tenant with 'teams-phone tenants add' which auto-sets "
                    "the first tenant as default."
                ),
            )
        return self.config_manager.get_tenant(default_name)

    def add_tenant(self, profile: TenantProfile) -> TenantProfile:
        """Add a new tenant profile.

        If this is the first tenant being added, it is automatically set
        as the default tenant.

        Args:
            profile: The TenantProfile to add.

        Returns:
            The added TenantProfile (same as input).

        Raises:
            ValidationError: If profile.name doesn't match the key being set.
        """
        config = self.config_manager.load_config()
        is_first_tenant = len(config.tenants) == 0

        self.config_manager.set_tenant(profile.name, profile)

        if is_first_tenant:
            self.config_manager.set_default_tenant(profile.name)

        return profile

    def remove_tenant(self, name: str) -> None:
        """Remove a tenant profile by name.

        Checks if the tenant is currently authenticated and logs the status.
        Note: Authentication tokens are not automatically cleared; they will
        expire naturally or can be cleared with 'teams-phone auth logout'.

        Args:
            name: The name of the tenant profile to remove.

        Raises:
            NotFoundError: If no tenant with the given name exists.
        """
        # Check auth status (informational, doesn't block removal)
        # is_authenticated never throws, safe to call
        _is_authenticated = self.auth_service.is_authenticated(name)

        # Delegate to ConfigManager which handles:
        # - NotFoundError if tenant doesn't exist
        # - Clearing default_tenant if this was the default
        self.config_manager.remove_tenant(name)

    def switch_tenant(self, name: str, *, auto_login: bool = True) -> TenantProfile:
        """Switch to a different tenant profile.

        Validates the tenant exists, checks authentication status,
        optionally performs auto-login, and updates the default tenant.

        Args:
            name: Name of the tenant profile to switch to.
            auto_login: If True (default), automatically login if not authenticated.

        Returns:
            The TenantProfile that was switched to.

        Raises:
            NotFoundError: If the tenant does not exist.
            AuthenticationError: If auto_login is True and login fails.
        """
        # Validate tenant exists (raises NotFoundError if not)
        profile = self.config_manager.get_tenant(name)

        # Check authentication status (is_authenticated never throws)
        if not self.auth_service.is_authenticated(name):
            if auto_login:
                # Attempt login - let AuthenticationError propagate if it fails
                # This ensures default_tenant is NOT updated on auth failure
                self.auth_service.login(name)

        # Only update default after successful auth check/login
        self.config_manager.set_default_tenant(name)

        return profile

    def test_connection(self, name: str) -> ConnectionTestResult:
        """Test connectivity to Microsoft Graph API for a tenant.

        Attempts token acquisition and makes a test API call to validate
        that the tenant configuration works correctly. This method never
        raises exceptions - all errors are captured in the result.

        Args:
            name: Name of the tenant profile to test.

        Returns:
            ConnectionTestResult with success status, latency, permissions,
            and error message if the test failed.
        """
        start_time = time.perf_counter()

        try:
            # Validate tenant exists (raises NotFoundError if not)
            self.config_manager.get_tenant(name)

            # Attempt token acquisition with force=True for fresh token
            cached_token = self.auth_service.login(name, force=True)

            # Make test Graph API call to /organization endpoint
            # Using sync httpx client since TenantService is synchronous
            headers = {"Authorization": f"Bearer {cached_token.access_token}"}
            with httpx.Client(timeout=30.0) as client:
                response = client.get(
                    f"{GRAPH_API_V1_BASE}/organization",
                    headers=headers,
                )
                response.raise_for_status()

            # Calculate latency
            elapsed_ms = int((time.perf_counter() - start_time) * 1000)

            # Return scopes from the token as permissions
            return ConnectionTestResult(
                success=True,
                tenant_name=name,
                latency_ms=elapsed_ms,
                permissions=cached_token.scopes,
            )

        except Exception as e:  # noqa: BLE001
            # Calculate latency even on failure (if we got past initial validation)
            elapsed_ms = int((time.perf_counter() - start_time) * 1000)

            return ConnectionTestResult(
                success=False,
                tenant_name=name,
                latency_ms=elapsed_ms if elapsed_ms > 0 else None,
                error_message=str(e),
            )
