from datetime import datetime, timedelta
from typing import Optional
from jose import jwt, JWTError
from analogai.foundation.modules.user.user import User
from analogai.foundation.modules.user.user_service import UserService
from analogai.foundation.common.logging.app_logger import app_logger
from analogai.foundation.extensions.authentication.core import IJWTService
from analogai.foundation.extensions.authentication.models import TokenPayload

class Auth0JWTService(IJWTService):
    def __init__(self, user_service: UserService):
        self.user_service = user_service

    def _redact_token(self, token: str) -> str:
        if not token:
            return "<empty>"
        if len(token) <= 10:
            return f"{token[0]}***{token[-1]}"
        return f"{token[:6]}...{token[-4:]}"

    def create_token_for_user(self, user_id: str, provider: str = "analogai") -> str:
        user = self.user_service.find(user_id)
        if not user or not user.email:
            raise ValueError(f"User with ID {user_id} not found or has no email")
        expire = datetime.utcnow() + timedelta(minutes=30)
        to_encode = {
            "sub": f"{provider}|{user_id}",
            "email": user.email,
            "name": user.name,
            "exp": expire,
            "iat": datetime.utcnow(),
            "iss": "analogai-chat",
            "aud": "analogai-chat-api",
        }
        return jwt.encode(to_encode, "test-secret-key", algorithm="HS256")

    def authenticate_user(self, token: str) -> Optional[User]:
        if not token:
            app_logger.debug("Authentication failed: missing auth token")
            app_logger.error("Authentication failed: auth token is not provided")
            raise ValueError("Missing auth token")
        try:
            payload = self.extract_payload(token)
            user = self.user_service.find_by_email(payload.email)
            if not user:
                raise ValueError("User not found for token")
            app_logger.info(f"Authenticated user {user.email} via JWT token")
            return user
        except ValueError as error:
            app_logger.debug(
                "Authentication failed for token %s: %s",
                self._redact_token(token),
                str(error),
            )
            app_logger.error(f"Authentication failed: {str(error)}")
            raise
        except Exception as error:
            app_logger.debug(
                "Authentication failed with unexpected error for token %s: %s",
                self._redact_token(token),
                str(error),
            )
            app_logger.error(f"Authentication failed with unexpected error: {str(error)}")
            raise ValueError("Authentication failed") from error

    def extract_payload(self, token: str) -> TokenPayload:
        payload = self.decode_payload(token)

        email = payload.get("email")
        if not email:
            app_logger.debug(
                "JWT payload missing email claim for token %s",
                self._redact_token(token),
            )
            raise ValueError("No 'email' claim in token")

        subject = payload.get("sub", "")
        provider, user_id = self._parse_subject(subject)

        user = self.user_service.find_by_email(email)
        resolved_user_id = user.id if user else user_id
        resolved_name = user.name if user else payload.get("name")

        app_logger.debug(f"Extracted payload for email '{email}' with provider '{provider}'")
        return TokenPayload(
            token=token,
            email=email,
            user_id=resolved_user_id,
            name=resolved_name,
            provider=provider,
        )

    def decode_payload(self, token: str) -> dict:
        try:
            return jwt.decode(
                token,
                key="dummy-key",
                options={
                    "verify_signature": False,
                    "verify_aud": False,
                    "verify_iss": False,
                    "verify_exp": False,
                },
            )
        except JWTError as error:
            app_logger.debug(
                "JWT parsing failed for token %s: %s",
                self._redact_token(token),
                str(error),
            )
            app_logger.error(f"JWT parsing failed: {str(error)}")
            raise ValueError("Invalid token format") from error

    def _parse_subject(self, subject: str) -> tuple[Optional[str], Optional[str]]:
        if not subject:
            return None, None
        if "|" in subject:
            provider, user_id = subject.split("|", 1)
            return provider, user_id
        return None, subject

