from datetime import datetime, timedelta, timezone
from fastapi import HTTPException, status, Request
from sqlmodel import Session

from app.models.user import User, Role
from app.models.user_session import UserSession
from app.repositories.user_repo import UserRepository
from app.repositories.user_session_repo import UserSessionRepository
from app.core.security import hash_password, verify_password
from app.utils.tokens import create_access_token, create_refresh_token, decode_token
from app.schemas.user import UserCreate
from app.core.config import get_settings
from app.core.constants import (
    ERR_EMAIL_REGISTERED,
    ERR_INVALID_CREDENTIALS,
    ERR_INVALID_TOKEN,
    ERR_REFRESH_TOKEN,
    ERR_ONLY_SUPERADMIN_CAN_CREATE_ADMINS
)

settings = get_settings()

class AuthService:
    """Service exposing auth-related business operations."""

    @staticmethod
    def signup(data: UserCreate, session: Session):
        """Create a new standard user and return tokens."""
        existing = UserRepository.get_by_email(session, data.email)
        if existing:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=ERR_EMAIL_REGISTERED)

        user = User(
            email=data.email,
            first_name=data.first_name,
            last_name=data.last_name,
            hashed_password=hash_password(data.password),
            phone=data.phone,
            role=Role.USERS,
        )

        user = UserRepository.create(session, user)
        return {"message": "User successfully created"}

    @staticmethod
    def login(email: str, password: str, request: Request, session: Session):
        user = UserRepository.get_by_email(session, email)
        if not user or not verify_password(password, user.hashed_password):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=ERR_INVALID_CREDENTIALS,
            )
        # CLEANUP FIRST
        UserSessionRepository.cleanup_user_sessions(session, user.id)
        # 1. Create refresh token
        refresh_token = create_refresh_token(sub=user.email)
        # 2. Create session FIRST
        user_session = UserSession(
            user_id=user.id,
            refresh_token=refresh_token,
            user_agent=request.headers.get("user-agent"),
            ip_address=request.client.host if request.client else None,
            expires_at=datetime.now(timezone.utc) + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS),
        )
        UserSessionRepository.create(session, user_session)
        # 3. Bind access token to session_id
        access_token = create_access_token(sub=user.email, session_id=user_session.session_id)

        return {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "bearer"
        }


    @staticmethod
    def refresh_token(refresh_token: str, session: Session):
        def ensure_utc_aware(dt: datetime) -> datetime:
            if dt.tzinfo is None:
                return dt.replace(tzinfo=timezone.utc)
            return dt.astimezone(timezone.utc)
        payload = decode_token(refresh_token)
        if payload.get("type") != "refresh":
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=ERR_INVALID_TOKEN,
            )
        user_session = UserSessionRepository.get_by_refresh_token(session, refresh_token)
        if not user_session:
            # refresh token reuse detected -> revoke all
            sub = payload.get("sub")
            user = UserRepository.get_by_email(session, sub)
            if user:
                UserSessionRepository.revoke_all_for_user(session, user.id)
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=ERR_REFRESH_TOKEN
            )
        user = UserRepository.get_by_id(session, user_session.user_id)
        if payload.get("sub") != user.email:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=ERR_REFRESH_TOKEN)
        expires_at = ensure_utc_aware(user_session.expires_at)
        now = datetime.now(timezone.utc)
        remaining_ttl = expires_at - now
        if remaining_ttl.total_seconds() <= 0:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=ERR_REFRESH_TOKEN)
        # 1. Rotate refresh token
        new_refresh = create_refresh_token(sub=user.email, expires_delta=remaining_ttl)
        # 2. Revoke old session
        UserSessionRepository.revoke(session, user_session, replaced_by_token=new_refresh)
        # 3. Create new session
        new_session = UserSession(
            user_id=user.id,
            refresh_token=new_refresh,
            user_agent=user_session.user_agent,
            ip_address=user_session.ip_address,
            expires_at=user_session.expires_at
        )
        UserSessionRepository.create(session, new_session)
        # 4. Bind new access token to NEW session
        new_access = create_access_token(
            sub=user.email,
            session_id=new_session.session_id,
        )
        return {
            "access_token": new_access,
            "refresh_token": new_refresh,
            "token_type": "bearer"
        }

    @staticmethod
    def logout(refresh_token: str, session: Session):
        user_session = UserSessionRepository.get_by_refresh_token(session, refresh_token)

        if user_session:
            UserSessionRepository.revoke(session, user_session)

        return {"message": "Logged out successfully"}

    @staticmethod
    def logout_all(user: User, session: Session):
        UserSessionRepository.revoke_all_for_user(session, user.id)
        return {"message": "Logged out from all devices"}

    @staticmethod
    def register_admin(data: UserCreate, session: Session, current_super: User):
        """
        SUPERADMIN-only: create an ADMIN user.
        'current_super' param is passed from dependency checks in routes.
        """
        if current_super.role != Role.SUPERADMIN:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERR_ONLY_SUPERADMIN_CAN_CREATE_ADMINS)

        existing = UserRepository.get_by_email(session, data.email)
        if existing:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=ERR_EMAIL_REGISTERED)

        admin = User(
            email=data.email,
            first_name=data.first_name,
            last_name=data.last_name,
            hashed_password=hash_password(data.password),
            phone=data.phone,
            role=Role.ADMIN
        )
        admin = UserRepository.create(session, admin)
        return admin