"""
Casdoor-specific JWT token validation provider.
"""
from typing import Dict, Any, Optional, List
import json
from ..core.user import User
from ..core.config import CasdoorConfig
from ..core.errors import (
    AuthError,
    AuthProviderError,
    InvalidTokenError,
    WebhookException
)
from .jwt_provider import JWTProvider


class CasdoorProvider(JWTProvider):
    """
    Casdoor-specific JWT token validation provider.
    
    This provider extends the generic JWT provider with Casdoor-specific
    functionality and uses the Casdoor SDK when available.
    """
    
    def __init__(self, config: CasdoorConfig):
        """
        Initialize Casdoor provider with configuration.
        
        Args:
            config: Casdoor configuration object
        """
        self.casdoor_config = config
        self.casdoor_sdk = None
        super().__init__(config)
        self._initialize_casdoor_sdk()
    
    def _initialize_casdoor_sdk(self) -> None:
        """Initialize Casdoor SDK if available."""
        if not self.casdoor_config.is_configured():
            return
        
        try:
            from casdoor import CasdoorSDK
            
            self.casdoor_sdk = CasdoorSDK(
                endpoint=self.casdoor_config.endpoint,
                client_id=self.casdoor_config.client_id,
                client_secret=self.casdoor_config.client_secret,
                certificate=self.casdoor_config.certificate,
                org_name=self.casdoor_config.org_name,
                application_name=self.casdoor_config.app_name
            )
        except ImportError:
            # Casdoor SDK not available, fall back to generic JWT validation
            self.casdoor_sdk = None
        except Exception as e:
            raise AuthProviderError(f"Failed to initialize Casdoor SDK: {str(e)}")
    
    async def validate_token(self, token: str) -> User:
        """
        Validate Casdoor JWT token and return user information.
        
        Uses Casdoor SDK if available, otherwise falls back to generic JWT validation.
        
        Args:
            token: JWT token string
            
        Returns:
            User object with validated user information
            
        Raises:
            AuthError: If token validation fails
        """
        if not token:
            raise InvalidTokenError(details={"reason": "Empty token"})
        
        try:
            if self.casdoor_sdk:
                # Use Casdoor SDK for validation
                claims = self.casdoor_sdk.parse_jwt_token(token)
                if not claims:
                    raise InvalidTokenError(details={"reason": "Casdoor SDK returned no claims"})
                
                return self._extract_casdoor_user(claims)
            else:
                # Fall back to generic JWT validation
                return await super().validate_token(token)
                
        except AuthError:
            # Re-raise our own errors
            raise
        except Exception as e:
            raise AuthProviderError(f"Casdoor token validation failed: {str(e)}")
    
    def _extract_casdoor_user(self, claims: Dict[str, Any]) -> User:
        """
        Extract user information from Casdoor JWT claims.
        
        Args:
            claims: Casdoor JWT claims
            
        Returns:
            User object
            
        Raises:
            InvalidTokenError: If required user fields are missing
        """
        # Casdoor-specific claim extraction
        user_id = claims.get("id") or claims.get("sub")
        email = claims.get("email")
        name = claims.get("name")
        display_name = claims.get("displayName")
        avatar = claims.get("avatar")
        
        if not user_id:
            raise InvalidTokenError(details={"reason": "Missing user ID in Casdoor token"})
        
        if not email:
            raise InvalidTokenError(details={"reason": "Missing email in Casdoor token"})
        
        if not name:
            name = email  # Fallback to email
        
        # Extract Casdoor-specific metadata
        metadata = {
            "casdoor": {
                "owner": claims.get("owner"),
                "org": claims.get("org"),
                "application": claims.get("application"),
                "iss": claims.get("iss"),
                "aud": claims.get("aud"),
                "exp": claims.get("exp"),
                "iat": claims.get("iat")
            },
            "jwt_payload": claims,
            "provider": self.get_provider_name()
        }
        
        # Casdoor doesn't typically have roles/permissions in JWT
        # These would usually come from separate API calls
        roles = []
        permissions = []
        
        return User(
            id=str(user_id),
            email=str(email),
            name=str(name),
            display_name=display_name,
            avatar=avatar,
            roles=roles,
            permissions=permissions,
            metadata=metadata
        )
    
    def get_provider_name(self) -> str:
        """Get the name of this authentication provider."""
        return "casdoor"
    
    def get_provider_version(self) -> Optional[str]:
        """Get the version of the Casdoor SDK if available."""
        if self.casdoor_sdk:
            try:
                import casdoor
                return casdoor.__version__
            except AttributeError:
                return "unknown"
        return "sdk_not_available"
    
    def is_configured(self) -> bool:
        """Check if this provider is properly configured."""
        return self.casdoor_config.is_configured()
    
    async def health_check(self) -> bool:
        """
        Perform a health check on the Casdoor provider.
        
        This checks configuration and optionally tests connectivity
        to the Casdoor endpoint.
        """
        if not self.is_configured():
            return False
        
        # Basic configuration check
        base_health = await super().health_check()
        if not base_health:
            return False
        
        # If we have SDK, we could test connectivity
        # For now, just return the base health check
        return True
    
    def validate_webhook(self, 
                        webhook_data: Dict[str, Any], 
                        allowed_events: Optional[List[str]] = None,
                        expected_application: Optional[str] = None,
                        expected_organization: Optional[str] = None) -> Dict[str, Any]:
        """
        Validate Casdoor webhook and extract user data from extendedUser.
        
        Args:
            webhook_data: Raw webhook JSON data as dict
            allowed_events: List of allowed event types (defaults to ['login', 'signup'])
            expected_application: Expected application name (uses config if None)
            expected_organization: Expected organization name (uses config if None)
            
        Returns:
            Dict: Complete extendedUser object with all Casdoor user fields
            
        Raises:
            WebhookException: With httpResponse property for any validation failures
        """
        
        # Set defaults
        if allowed_events is None:
            allowed_events = ['login', 'signup']
        if expected_application is None:
            expected_application = self.casdoor_config.app_name
        if expected_organization is None:
            expected_organization = self.casdoor_config.org_name
        
        # 1. Parse and validate object field (for application check)
        object_str = webhook_data.get('object', '{}')
        try:
            object_data = json.loads(object_str) if isinstance(object_str, str) else object_str
        except json.JSONDecodeError:
            raise WebhookException(400, "Invalid JSON in webhook 'object' field")
        
        # 2. Validate application FIRST (quick exit for different apps)
        webhook_app = object_data.get('application')
        if expected_application and webhook_app and webhook_app != expected_application:
            raise WebhookException(
                200,
                f"Webhook for different application: {webhook_app}",
                status="ignored"
            )
        
        # 3. Validate organization SECOND (quick exit for different orgs)
        webhook_org = webhook_data.get('organization')
        if expected_organization and webhook_org and webhook_org != expected_organization:
            raise WebhookException(
                200,
                f"Webhook for different organization: {webhook_org}", 
                status="ignored"
            )
        
        # 4. Validate event type
        action = webhook_data.get('action')
        if not action:
            raise WebhookException(400, "Missing 'action' field in webhook data")
        
        if action not in allowed_events:
            raise WebhookException(
                200, 
                f"Event type '{action}' is not processed",
                status="ignored"
            )
        
        # 5. Require extendedUser data
        extended_user = webhook_data.get('extendedUser')
        if not extended_user:
            raise WebhookException(
                400,
                "Missing user data. Please enable 'Is user extended' in Casdoor webhook settings for this webhook endpoint."
            )
        
        # 6. Extract and validate user data from extendedUser
        user_id = extended_user.get('id')
        email = extended_user.get('email')
        
        if not user_id:
            raise WebhookException(400, "Missing user ID in extendedUser data")
        if not email:
            raise WebhookException(400, "Missing email in extendedUser data")
        
        # Return only the complete extendedUser data
        return extended_user
    
    def get_casdoor_endpoint(self) -> str:
        """Get the Casdoor endpoint URL."""
        return self.casdoor_config.endpoint
    
    def get_casdoor_org(self) -> str:
        """Get the Casdoor organization name."""
        return self.casdoor_config.org_name
    
    def get_casdoor_app(self) -> str:
        """Get the Casdoor application name."""
        return self.casdoor_config.app_name
