# [DEF:backend.src.services.auth_service:Module]
#
# @TIER:      CRITICAL
# @SEMANTICS: auth, service, business-logic, login, jwt, adfs, jit-provisioning
# @PURPOSE:   Orchestrates credential authentication and ADFS JIT user provisioning.
# @LAYER:     Domain
# @RELATION:  [DEPENDS_ON] ->[backend.src.core.auth.repository.AuthRepository]
# @RELATION:  [DEPENDS_ON] ->[backend.src.core.auth.security.verify_password]
# @RELATION:  [DEPENDS_ON] ->[backend.src.core.auth.jwt.create_access_token]
# @RELATION:  [DEPENDS_ON] ->[backend.src.models.auth.User]
# @RELATION:  [DEPENDS_ON] ->[backend.src.models.auth.Role]
#
# @INVARIANT: Authentication succeeds only for active users with valid credentials; issued sessions encode subject and scopes from assigned roles.

# [SECTION: IMPORTS]
from typing import Dict, Any
from sqlalchemy.orm import Session
from ..models.auth import User, Role
from ..core.auth.repository import AuthRepository
from ..core.auth.security import verify_password
from ..core.auth.jwt import create_access_token
from ..core.logger import belief_scope
# [/SECTION]

# [DEF:AuthService:Class]
# @PURPOSE: Provides high-level authentication services.
class AuthService:
    # [DEF:__init__:Function]
    # @PURPOSE: Initializes the authentication service with repository access over an active DB session.
    # @PRE: db is a valid SQLAlchemy Session instance bound to the auth persistence context.
    # @POST: self.repo is initialized and ready for auth user/role CRUD operations.
    # @SIDE_EFFECT: Allocates AuthRepository and binds it to the provided Session.
    # @DATA_CONTRACT: Input(Session) -> Model(AuthRepository)
    # @PARAM: db (Session) - SQLAlchemy session.
    def __init__(self, db: Session):
        self.repo = AuthRepository(db)
    # [/DEF:__init__:Function]

    # [DEF:authenticate_user:Function]
    # @PURPOSE: Validates credentials and account state for local username/password authentication.
    # @PRE: username and password are non-empty credential inputs.
    # @POST: Returns User only when user exists, is active, and password hash verification succeeds; otherwise returns None.
    # @SIDE_EFFECT: Persists last_login update for successful authentications via repository.
    # @DATA_CONTRACT: Input(str username, str password) -> Output(User | None)
    # @PARAM: username (str) - The username.
    # @PARAM: password (str) - The plain password.
    # @RETURN: Optional[User] - The authenticated user or None.
    def authenticate_user(self, username: str, password: str):
        with belief_scope("AuthService.authenticate_user"):
            user = self.repo.get_user_by_username(username)
            if not user:
                return None
            
            if not user.is_active:
                return None
                
            if not user.password_hash or not verify_password(password, user.password_hash):
                return None
            
            self.repo.update_last_login(user)
            return user
    # [/DEF:authenticate_user:Function]

    # [DEF:create_session:Function]
    # @PURPOSE: Issues an access token payload for an already authenticated user.
    # @PRE: user is a valid User entity containing username and iterable roles with role.name values.
    # @POST: Returns session dict with non-empty access_token and token_type='bearer'.
    # @SIDE_EFFECT: Generates signed JWT via auth JWT provider.
    # @DATA_CONTRACT: Input(User) -> Output(Dict[str, str]{access_token, token_type})
    # @PARAM: user (User) - The authenticated user.
    # @RETURN: Dict[str, str] - Session data.
    def create_session(self, user) -> Dict[str, str]:
        with belief_scope("AuthService.create_session"):
            # Collect role names for scopes
            scopes = [role.name for role in user.roles]
            
            token_data = {
                "sub": user.username,
                "scopes": scopes
            }
            
            access_token = create_access_token(data=token_data)
            return {
                "access_token": access_token,
                "token_type": "bearer"
            }
    # [/DEF:create_session:Function]

    # [DEF:provision_adfs_user:Function]
    # @PURPOSE: Performs ADFS Just-In-Time provisioning and role synchronization from AD group mappings.
    # @PRE: user_info contains identity claims where at least one of 'upn' or 'email' is present; 'groups' may be absent.
    # @POST: Returns persisted user entity with roles synchronized to mapped AD groups and refreshed state.
    # @SIDE_EFFECT: May insert new User, mutate user.roles, commit transaction, and refresh ORM state.
    # @DATA_CONTRACT: Input(Dict[str, Any]{upn|email, email, groups[]}) -> Output(User persisted)
    # @PARAM: user_info (Dict[str, Any]) - Claims from ADFS token.
    # @RETURN: User - The provisioned user.
    def provision_adfs_user(self, user_info: Dict[str, Any]) -> User:
        with belief_scope("AuthService.provision_adfs_user"):
            username = user_info.get("upn") or user_info.get("email")
            email = user_info.get("email")
            ad_groups = user_info.get("groups", [])
            
            user = self.repo.get_user_by_username(username)
            if not user:
                user = User(
                    username=username,
                    email=email,
                    auth_source="ADFS",
                    is_active=True
                )
                self.repo.db.add(user)
            
            # Update roles based on group mappings
            from ..models.auth import ADGroupMapping
            mapped_roles = self.repo.db.query(Role).join(ADGroupMapping).filter(
                ADGroupMapping.ad_group.in_(ad_groups)
            ).all()
            
            user.roles = mapped_roles
            self.repo.db.commit()
            self.repo.db.refresh(user)
            return user
    # [/DEF:provision_adfs_user:Function]

# [/DEF:AuthService:Class]

# [/DEF:backend.src.services.auth_service:Module]