"""سرویس احراز هویت: ثبت‌نام خودکار و صدور توکن."""
from __future__ import annotations

from sqlalchemy.ext.asyncio import AsyncSession

from app.core.config import settings
from app.models.audit import AuditLog
from app.models.enums import UserRole, UserStatus
from app.models.user import User
from app.repositories.user import UserRepository
from app.schemas.auth import OtpRequestResponse, OtpVerifyPayload, TokenResponse
from app.services.otp_service import OtpService
from app.services.payamak_client import SmsDeliveryError
from app.services.token_service import TokenService
from shared.exceptions import AppError, UnauthorizedError


class SmsSendError(AppError):
    status_code = 502
    error_code = "sms_delivery_failed"


class AuthService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.users = UserRepository(session)
        self.otp = OtpService()
        self.tokens = TokenService()

    async def request_otp(self, mobile: str, *, force_sms: bool = False, context: str = "public") -> OtpRequestResponse:
        try:
            expires_in, skipped = await self.otp.request_otp(mobile, force=force_sms)
        except SmsDeliveryError as exc:
            raise SmsSendError(str(exc)) from exc
        message = "کد قبلاً ارسال شده" if skipped else "کد تأیید ارسال شد"
        return OtpRequestResponse(
            message=message,
            expires_in=expires_in,
            skipped=skipped,
        )

    async def verify_otp(self, payload: OtpVerifyPayload) -> TokenResponse:
        if not await self.otp.verify_otp(payload.mobile, payload.code):
            raise UnauthorizedError("کد تأیید نامعتبر یا منقضی شده است")

        user = await self.users.get_by_mobile(payload.mobile)
        is_new_user = user is None
        is_platform_admin = payload.mobile in settings.platform_admin_mobile_set

        if is_new_user:
            if is_platform_admin:
                role = UserRole.PLATFORM_ADMIN
            elif payload.context == "admin":
                role = UserRole.PENDING_TENANT_ADMIN
            else:
                role = UserRole.USER
            user = User(
                mobile=payload.mobile,
                role=role,
                status=UserStatus.ACTIVE,
                mobile_verified=True,
                keycloak_sub=payload.keycloak_sub,
                email=payload.email,
            )
            await self.users.add(user)
            await self._log_registration(user)
            await self.session.commit()
            await self.session.refresh(user)
        else:
            if user.status != UserStatus.ACTIVE:
                raise UnauthorizedError("حساب کاربری غیرفعال است")
            if is_platform_admin and user.role != UserRole.PLATFORM_ADMIN:
                user.role = UserRole.PLATFORM_ADMIN
            user.mobile_verified = True
            if payload.keycloak_sub and not user.keycloak_sub:
                user.keycloak_sub = payload.keycloak_sub
            if payload.email and not user.email:
                user.email = payload.email
            await self.session.commit()
            await self.session.refresh(user)

        access_token, expires_in = self.tokens.create_access_token(
            user_id=user.id,
            mobile=user.mobile,
            role=user.role,
        )
        refresh_token = self.tokens.create_refresh_token(user.id)

        return TokenResponse(
            access_token=access_token,
            refresh_token=refresh_token,
            expires_in=expires_in,
            is_new_user=is_new_user,
            user_id=user.id,
            role=user.role.value,
        )

    async def _log_registration(self, user: User) -> None:
        log = AuditLog(
            tenant_id=None,
            actor_user_id=user.id,
            action="user.registered",
            resource_type="user",
            resource_id=str(user.id),
            meta_data={"mobile": user.mobile, "role": user.role.value},
        )
        self.session.add(log)
