"""Microsoft Teams channel implementation.

This module provides the Teams channel implementation using
the Bot Framework SDK.
"""

import logging
import os
from datetime import datetime
from typing import Any, Dict, Optional

from agent_factory.channels.base import (
    BaseChannel,
    ChannelAuthError,
    ChannelError,
    ChannelMessage,
    ChannelResponse,
    ChannelType,
)

logger = logging.getLogger(__name__)


class TeamsChannel(BaseChannel):
    """Microsoft Teams channel implementation.

    Handles incoming messages from Teams and sends responses
    using the Bot Framework.

    Example:
        channel = TeamsChannel(
            app_id="your-app-id",
            app_password="your-app-password",
        )

        # Handle incoming activity
        message = channel.handle_event(activity)

        # Send response
        channel.send_message(
            conversation_id="...",
            text="Hello from the bot!",
        )
    """

    def __init__(
        self,
        app_id: Optional[str] = None,
        app_password: Optional[str] = None,
        tenant_id: Optional[str] = None,
    ):
        """Initialize Teams channel.

        Args:
            app_id: Microsoft App ID (or TEAMS_APP_ID env var)
            app_password: Microsoft App Password (or TEAMS_APP_PASSWORD env var)
            tenant_id: Azure AD Tenant ID (or TEAMS_TENANT_ID env var)
        """
        self.app_id = app_id or os.getenv("TEAMS_APP_ID")
        self.app_password = app_password or os.getenv("TEAMS_APP_PASSWORD")
        self.tenant_id = tenant_id or os.getenv("TEAMS_TENANT_ID")
        self._access_token: Optional[str] = None
        self._token_expires: Optional[datetime] = None

    @property
    def channel_type(self) -> ChannelType:
        """Get the channel type."""
        return ChannelType.TEAMS

    def handle_event(
        self,
        event: Dict[str, Any],
        raw_body: Optional[str] = None,
        auth_header: Optional[str] = None,
    ) -> Optional[ChannelMessage]:
        """Handle an incoming Teams activity.

        Args:
            event: Teams activity object
            raw_body: Raw request body (for signature verification)
            auth_header: Authorization header from request

        Returns:
            ChannelMessage if this is a message event, None otherwise
        """
        activity_type = event.get("type", "")

        # Handle different activity types
        if activity_type == "message":
            return self._handle_message(event)
        elif activity_type == "conversationUpdate":
            return self._handle_conversation_update(event)
        elif activity_type == "invoke":
            return self._handle_invoke(event)
        else:
            logger.debug(f"Ignoring activity type: {activity_type}")
            return None

    def _handle_message(self, activity: Dict[str, Any]) -> Optional[ChannelMessage]:
        """Handle a message activity."""
        text = activity.get("text", "")

        # Remove bot mention from text
        if activity.get("entities"):
            for entity in activity["entities"]:
                if entity.get("type") == "mention":
                    mentioned = entity.get("mentioned", {})
                    if mentioned.get("id") == self.app_id:
                        # Remove the mention text
                        mention_text = entity.get("text", "")
                        text = text.replace(mention_text, "").strip()

        if not text:
            return None

        from_user = activity.get("from", {})
        conversation = activity.get("conversation", {})

        return ChannelMessage(
            channel_type=ChannelType.TEAMS,
            user_id=from_user.get("id", ""),
            user_name=from_user.get("name"),
            channel_id=conversation.get("id", ""),
            text=text,
            thread_id=conversation.get("id"),
            raw_event=activity,
            metadata={
                "activity_id": activity.get("id"),
                "service_url": activity.get("serviceUrl"),
                "tenant_id": conversation.get("tenantId"),
                "conversation_type": conversation.get("conversationType"),
            },
        )

    def _handle_conversation_update(self, activity: Dict[str, Any]) -> Optional[ChannelMessage]:
        """Handle conversation update (member added/removed)."""
        members_added = activity.get("membersAdded", [])

        # Check if bot was added to conversation
        for member in members_added:
            if member.get("id") == self.app_id:
                logger.info("Bot added to conversation")
                # Could return a special message to trigger welcome
                return None

        return None

    def _handle_invoke(self, activity: Dict[str, Any]) -> Optional[ChannelMessage]:
        """Handle invoke activity (adaptive cards, etc.)."""
        invoke_name = activity.get("name", "")
        logger.debug(f"Invoke activity: {invoke_name}")
        return None

    def send_message(
        self,
        conversation_id: str,
        text: str,
        service_url: Optional[str] = None,
        reply_to_id: Optional[str] = None,
        attachments: Optional[list] = None,
        **kwargs,
    ) -> ChannelResponse:
        """Send a message to Teams.

        Args:
            conversation_id: Teams conversation ID
            text: Message text
            service_url: Bot Framework service URL
            reply_to_id: Activity ID to reply to
            attachments: List of adaptive card attachments

        Returns:
            ChannelResponse with send result
        """
        try:
            import httpx
        except ImportError:
            raise ChannelError(
                "httpx is required for Teams. Install with: pip install agent-factory-core[teams]",
                channel_type=ChannelType.TEAMS,
            )

        if not service_url:
            raise ChannelError(
                "service_url is required to send Teams messages",
                channel_type=ChannelType.TEAMS,
            )

        # Get access token
        token = self._get_access_token()

        # Build activity
        activity = {
            "type": "message",
            "text": text,
            "conversation": {"id": conversation_id},
        }

        if reply_to_id:
            activity["replyToId"] = reply_to_id

        if attachments:
            activity["attachments"] = attachments

        # Send to Bot Framework
        url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"

        try:
            with httpx.Client() as client:
                response = client.post(
                    url,
                    json=activity,
                    headers={
                        "Authorization": f"Bearer {token}",
                        "Content-Type": "application/json",
                    },
                    timeout=30.0,
                )
                response.raise_for_status()
                result = response.json()

            return ChannelResponse(
                success=True,
                message_id=result.get("id"),
                channel_type=ChannelType.TEAMS,
                raw_response=result,
            )

        except httpx.HTTPStatusError as e:
            logger.exception("Teams API error")
            return ChannelResponse(
                success=False,
                error=f"Teams API error: {e.response.status_code}",
                channel_type=ChannelType.TEAMS,
            )
        except Exception as e:
            logger.exception("Failed to send Teams message")
            return ChannelResponse(
                success=False,
                error=str(e),
                channel_type=ChannelType.TEAMS,
            )

    def send_adaptive_card(
        self,
        conversation_id: str,
        card: Dict[str, Any],
        service_url: str,
        fallback_text: str = "This message contains an adaptive card.",
    ) -> ChannelResponse:
        """Send an Adaptive Card to Teams.

        Args:
            conversation_id: Teams conversation ID
            card: Adaptive Card JSON
            service_url: Bot Framework service URL
            fallback_text: Fallback text for clients that don't support cards

        Returns:
            ChannelResponse with send result
        """
        attachment = {
            "contentType": "application/vnd.microsoft.card.adaptive",
            "content": card,
        }

        return self.send_message(
            conversation_id=conversation_id,
            text=fallback_text,
            service_url=service_url,
            attachments=[attachment],
        )

    def _get_access_token(self) -> str:
        """Get Bot Framework access token.

        Returns:
            Access token string
        """
        # Check if we have a valid cached token
        if self._access_token and self._token_expires:
            if datetime.now() < self._token_expires:
                return self._access_token

        if not self.app_id or not self.app_password:
            raise ChannelAuthError(
                "TEAMS_APP_ID and TEAMS_APP_PASSWORD are required",
                channel_type=ChannelType.TEAMS,
            )

        try:
            import httpx
        except ImportError:
            raise ChannelError(
                "httpx is required for Teams",
                channel_type=ChannelType.TEAMS,
            )

        # Get token from Azure AD
        token_url = "https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token"

        data = {
            "grant_type": "client_credentials",
            "client_id": self.app_id,
            "client_secret": self.app_password,
            "scope": "https://api.botframework.com/.default",
        }

        try:
            with httpx.Client() as client:
                response = client.post(
                    token_url,
                    data=data,
                    timeout=30.0,
                )
                response.raise_for_status()
                result = response.json()

            self._access_token = result["access_token"]
            # Cache token with some buffer before expiry
            expires_in = result.get("expires_in", 3600) - 300
            from datetime import timedelta

            self._token_expires = datetime.now() + timedelta(seconds=expires_in)

            return self._access_token

        except Exception as e:
            logger.exception("Failed to get Teams access token")
            raise ChannelAuthError(
                f"Failed to get access token: {e}",
                channel_type=ChannelType.TEAMS,
            )

    def verify_request(
        self,
        auth_header: str,
        body: str,
    ) -> bool:
        """Verify a request from Bot Framework.

        Note: Full JWT validation requires additional libraries.
        This is a simplified check.

        Args:
            auth_header: Authorization header
            body: Request body

        Returns:
            True if valid
        """
        if not auth_header:
            return False

        # Basic check - should start with Bearer
        if not auth_header.startswith("Bearer "):
            return False

        # Full validation would require:
        # 1. Fetch OpenID configuration
        # 2. Validate JWT signature
        # 3. Check claims (iss, aud, etc.)
        # For production, use botbuilder-core library

        return True

    def format_response(self, text: str, **kwargs) -> str:
        """Format text for Teams display.

        Args:
            text: Raw text

        Returns:
            Teams-formatted text
        """
        # Teams supports markdown
        return text
