"""
Authentication module for PowerBI API

Handles Azure AD OAuth2 client credentials flow for service principal authentication.
"""

import os
import logging
from datetime import datetime, timedelta
from typing import Optional

import httpx
from dotenv import load_dotenv

from .constants import AUTH_URL_TEMPLATE, AUTH_SCOPE, AUTH_TIMEOUT, TOKEN_REFRESH_BUFFER
from .exceptions import handle_http_error

logger = logging.getLogger(__name__)

# Load environment variables from current working directory (where the MCP client is running)
# This allows .env files to be project-specific rather than server-specific
load_dotenv(dotenv_path=os.path.join(os.getcwd(), '.env'), override=False)
logger.info(f"Attempted to load .env from: {os.getcwd()}")


class PowerBIAuth:
    """Handles PowerBI authentication using client credentials flow"""

    def __init__(
        self,
        tenant_id: Optional[str] = None,
        client_id: Optional[str] = None,
        client_secret: Optional[str] = None
    ):
        """
        Initialize authentication handler

        Args:
            tenant_id: Azure AD tenant ID (defaults to POWERBI_TENANT_ID env var)
            client_id: Application client ID (defaults to POWERBI_CLIENT_ID env var)
            client_secret: Application client secret (defaults to POWERBI_CLIENT_SECRET env var)

        Note:
            Credentials can be None at initialization. Validation happens when
            authentication is attempted (when tools are actually used).
        """
        self.tenant_id = tenant_id or os.getenv("POWERBI_TENANT_ID")
        self.client_id = client_id or os.getenv("POWERBI_CLIENT_ID")
        self.client_secret = client_secret or os.getenv("POWERBI_CLIENT_SECRET")

        self.access_token: Optional[str] = None
        self.token_expires: Optional[datetime] = None
        self.http_client = httpx.AsyncClient(timeout=AUTH_TIMEOUT)

        if all([self.tenant_id, self.client_id, self.client_secret]):
            logger.info("PowerBI authentication handler initialized with credentials")
        else:
            logger.warning(
                "PowerBI authentication handler initialized without credentials. "
                "Set POWERBI_TENANT_ID, POWERBI_CLIENT_ID, and POWERBI_CLIENT_SECRET "
                "environment variables before using tools."
            )

    async def get_access_token(self) -> str:
        """
        Get valid access token, refreshing if necessary

        Returns:
            Valid access token string

        Raises:
            Exception: If authentication fails
        """
        if self.access_token and self.token_expires and datetime.now() < self.token_expires:
            logger.debug("Using cached access token")
            return self.access_token

        logger.info("Access token expired or missing, refreshing...")
        await self._authenticate()
        return self.access_token

    async def _authenticate(self) -> None:
        """
        Authenticate with Azure AD using client credentials

        Raises:
            Exception: If authentication fails with detailed error message
        """
        # Validate credentials before attempting authentication
        if not all([self.tenant_id, self.client_id, self.client_secret]):
            missing = []
            if not self.tenant_id:
                missing.append("POWERBI_TENANT_ID")
            if not self.client_id:
                missing.append("POWERBI_CLIENT_ID")
            if not self.client_secret:
                missing.append("POWERBI_CLIENT_SECRET")

            raise Exception(
                f"Missing required environment variables: {', '.join(missing)}. "
                "Please set these variables in your .env file or environment configuration."
            )

        auth_url = AUTH_URL_TEMPLATE.format(tenant_id=self.tenant_id)

        data = {
            "client_id": self.client_id,
            "client_secret": self.client_secret,
            "scope": AUTH_SCOPE,
            "grant_type": "client_credentials"
        }

        try:
            logger.debug(f"Authenticating with Azure AD at {auth_url}")
            response = await self.http_client.post(auth_url, data=data)
            response.raise_for_status()

            token_data = response.json()
            self.access_token = token_data["access_token"]
            expires_in = token_data.get("expires_in", 3600)
            # Refresh before expiry to avoid race conditions
            self.token_expires = datetime.now() + timedelta(seconds=expires_in - TOKEN_REFRESH_BUFFER)

            logger.info(f"Successfully authenticated with PowerBI. Token expires in {expires_in}s")

        except httpx.HTTPStatusError as e:
            # Use centralized error handler
            raise handle_http_error(e, "authentication") from e
        except Exception as e:
            logger.error(f"Authentication error: {e}")
            raise Exception(f"Failed to authenticate with PowerBI: {str(e)}") from e

    async def close(self) -> None:
        """Close HTTP client and cleanup resources"""
        await self.http_client.aclose()
        logger.info("Authentication handler closed")
