# app/modules/core/services/auth_service.py
"""
Authentication service for user login and store access control.

This module provides:
- User authentication and JWT token generation
- Store access verification
- Password hashing utilities

Note: Customer registration is handled by CustomerService.
      User (admin/store team) creation is handled by their respective services.
"""

from __future__ import annotations

import logging
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any

from sqlalchemy.orm import Session

from app.modules.tenancy.exceptions import (
    EmailNotVerifiedException,
    InvalidCredentialsException,
    StoreNotFoundException,
    UserNotActiveException,
)
from app.modules.tenancy.schemas.auth import UserLogin
from middleware.auth import AuthManager

if TYPE_CHECKING:
    from app.modules.tenancy.models import Store, User

logger = logging.getLogger(__name__)


class AuthService:
    """Service class for authentication operations."""

    def __init__(self):
        """Initialize with AuthManager instance."""
        self.auth_manager = AuthManager()

    def login_user(self, db: Session, user_credentials: UserLogin) -> dict[str, Any]:
        """
        Login user and return JWT token with user data.

        Args:
            db: Database session
            user_credentials: User login credentials

        Returns:
            Dictionary containing access token data and user object

        Raises:
            InvalidCredentialsException: If authentication fails
            UserNotActiveException: If user account is not active
        """
        user = self.auth_manager.authenticate_user(
            db, user_credentials.email_or_username, user_credentials.password
        )
        if not user:
            raise InvalidCredentialsException("Incorrect username or password")

        if not user.is_active:
            raise UserNotActiveException("User account is not active")

        if not user.is_email_verified:
            raise EmailNotVerifiedException()

        # Update last_login timestamp
        user.last_login = datetime.now(UTC)
        db.commit()  # SVC-006 - Login must persist last_login timestamp

        token_data = self.auth_manager.create_access_token(user)

        logger.info(f"User logged in: {user.username}")
        return {"token_data": token_data, "user": user}

    def hash_password(self, password: str) -> str:
        """
        Hash a password.

        Args:
            password: Plain text password

        Returns:
            Hashed password string
        """
        return self.auth_manager.hash_password(password)

    def get_store_by_code(self, db: Session, store_code: str) -> Store | None:
        """
        Get active store by store code.

        Args:
            db: Database session
            store_code: Store code to look up

        Returns:
            Store if found and active, None otherwise
        """
        from app.modules.tenancy.services.store_service import store_service

        try:
            return store_service.get_active_store_by_code(db, store_code)
        except StoreNotFoundException:
            return None

    def get_user_store_role(
        self, db: Session, user: User, store: Store
    ) -> tuple[bool, str | None]:
        """
        Check if user has access to store and return their role.

        Args:
            db: Database session
            user: User to check
            store: Store to check access for

        Returns:
            Tuple of (has_access: bool, role_name: str | None)
        """
        # Check if user is store owner (via merchant ownership)
        if store.merchant and store.merchant.owner_user_id == user.id:
            return True, "Owner"

        # Check if user is team member via team_service
        from app.modules.tenancy.services.team_service import team_service

        members = team_service.get_team_members(db, store.id, user)
        for member in members:
            if member["id"] == user.id and member["is_active"]:
                return True, member.get("role", "staff")

        return False, None

    def login_merchant(self, db: Session, user_credentials: UserLogin) -> dict[str, Any]:
        """
        Login merchant owner and return JWT token.

        Authenticates the user and verifies they own at least one active merchant.

        Args:
            db: Database session
            user_credentials: User login credentials

        Returns:
            Dictionary containing access token data and user object

        Raises:
            InvalidCredentialsException: If authentication fails
            UserNotActiveException: If user account is not active
        """
        user = self.auth_manager.authenticate_user(
            db, user_credentials.email_or_username, user_credentials.password
        )
        if not user:
            raise InvalidCredentialsException("Incorrect username or password")

        if not user.is_active:
            raise UserNotActiveException("User account is not active")

        if not user.is_email_verified:
            raise EmailNotVerifiedException()

        # Verify user owns at least one active merchant
        from app.modules.tenancy.services.merchant_service import merchant_service

        merchant_count = merchant_service.get_merchant_count_for_owner(db, user.id)

        if merchant_count == 0:
            raise InvalidCredentialsException(
                "No active merchant accounts found for this user"
            )

        # Update last_login timestamp
        user.last_login = datetime.now(UTC)
        db.commit()  # SVC-006 - Login must persist last_login timestamp

        token_data = self.auth_manager.create_access_token(user)

        logger.info(f"Merchant owner logged in: {user.username}")
        return {"token_data": token_data, "user": user}

    def find_user_store(self, user: User) -> tuple[Store | None, str | None]:
        """
        Find which store a user belongs to when no store context is provided.

        Checks owned merchants first, then store memberships.

        Args:
            user: User to find store for

        Returns:
            Tuple of (store: Store | None, role: str | None)
        """
        # Check owned stores first (via merchant ownership)
        for merchant in user.owned_merchants:
            if merchant.stores:
                return merchant.stores[0], "Owner"

        # Check store memberships
        if user.store_memberships:
            active_membership = next(
                (vm for vm in user.store_memberships if vm.is_active), None
            )
            if active_membership:
                role_name = active_membership.role.name if active_membership.role else "staff"
                return active_membership.store, role_name

        return None, None


# Create service instance
auth_service = AuthService()
