"""ورود/ثبت‌نام یکپارچه با موبایل — تشخیص خودکار login vs register."""
from __future__ import annotations

from datetime import datetime, timezone
from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.core.config import settings
from app.models.user import UserProfile
from app.repositories.user import UserRepository
from app.schemas.auth import (
    AuthSessionResponse,
    MobileAuthStartResponse,
    MobileOtpRequest,
    MobileOtpVerify,
    MobileRegisterVerify,
)
from app.services.auth_service import AuthService
from app.services.handoff_store import handoff_store
from app.services.keycloak_client import KeycloakAdminClient, mobile_keycloak_password
from app.services.otp_client import CoreOtpClient
from shared.exceptions import ConflictError, UnauthorizedError


class MobileAuthService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.users = UserRepository(session)
        self.keycloak = KeycloakAdminClient()
        self.otp = CoreOtpClient()
        self.auth = AuthService(session)

    async def start(self, data: MobileOtpRequest) -> MobileAuthStartResponse:
        existing = await self.users.get_by_mobile(data.mobile)
        if existing and existing.mobile_verified:
            intent = "login"
            default_message = "کد ورود ارسال شد"
        else:
            intent = "register"
            default_message = "کد ثبت‌نام ارسال شد"

        result = await self.otp.request_otp(data.mobile, force_sms=data.force_sms)
        return MobileAuthStartResponse(
            intent=intent,
            message=result.get("message", default_message),
            expires_in=result.get("expires_in", 120),
            skipped=result.get("skipped", False),
        )

    async def complete(self, data: MobileRegisterVerify) -> AuthSessionResponse:
        existing = await self.users.get_by_mobile(data.mobile)
        if existing and existing.mobile_verified:
            session = await self._complete_login(data, existing)
        else:
            session = await self._complete_register(data)
        return self._maybe_handoff(session, data.create_handoff)

    def _maybe_handoff(self, session: AuthSessionResponse, create_handoff: bool) -> AuthSessionResponse:
        if not create_handoff:
            return session
        code = handoff_store.create(session.model_dump())
        redirect = f"{settings.frontend_callback_url}?handoff={code}"
        return AuthSessionResponse(
            access_token="",
            refresh_token=None,
            expires_in=0,
            token_type="Bearer",
            message=session.message,
            intent=session.intent,
            user_id=session.user_id,
            mobile_verified=session.mobile_verified,
            redirect_url=redirect,
        )

    async def complete_login(self, data: MobileOtpVerify) -> AuthSessionResponse:
        profile = await self.users.get_by_mobile(data.mobile)
        if profile is None or not profile.mobile_verified:
            raise UnauthorizedError("حسابی با این شماره یافت نشد. ابتدا ثبت‌نام کنید")
        return await self._complete_login(data, profile)

    async def _complete_login(
        self, data: MobileOtpVerify, profile: UserProfile
    ) -> AuthSessionResponse:
        await self.otp.verify_otp(
            data.mobile,
            data.code,
            keycloak_sub=profile.keycloak_sub,
            email=profile.email,
        )
        await self._sync_platform_admin_role(profile)
        return await self.auth.issue_session_for_profile(
            profile,
            message="ورود با موفقیت انجام شد",
            intent="login",
            sync_mobile_password=True,
        )

    async def _complete_register(self, data: MobileRegisterVerify) -> AuthSessionResponse:
        if await self.users.get_by_mobile(data.mobile):
            raise ConflictError("این شماره موبایل قبلاً ثبت شده است", error_code="mobile_taken")

        has_credentials = bool(data.email and data.username and data.password)
        if has_credentials:
            if await self.users.get_by_username(data.username):
                raise ConflictError("نام کاربری تکراری است", error_code="username_taken")
            username = data.username
            email = str(data.email)
            password = data.password
        else:
            username = data.mobile
            email = f"{data.mobile[1:]}@mobile.torbatyar.local"
            password = mobile_keycloak_password(data.mobile)

        # اول Keycloak تا sub داشته باشیم؛ بعد OTP در Core با لینک SSO
        sub = await self.keycloak.create_user(
            email=email,
            username=username,
            password=password,
            display_name=data.display_name or data.username or data.mobile,
            mobile=data.mobile,
        )
        await self.keycloak.ensure_user_login_ready(sub)

        try:
            core_result = await self.otp.verify_otp(
                data.mobile,
                data.code,
                email=email,
                keycloak_sub=sub,
            )
        except Exception:
            # اگر OTP رد شد، کاربر نیمه‌کاره Keycloak را پاک نکنیم تا
            # retry ممکن باشد؛ فقط خطا را بالا می‌فرستیم.
            raise

        profile = await self.users.get_by_sub(sub)
        if profile is None:
            profile = UserProfile(
                keycloak_sub=sub,
                email=email,
                username=username,
                display_name=data.display_name or data.username,
                mobile=data.mobile,
                mobile_verified=True,
                mobile_verified_at=datetime.now(timezone.utc),
                core_user_id=UUID(core_result["user_id"]),
            )
            await self.users.add(profile)
        else:
            profile.email = email
            profile.username = username
            profile.mobile = data.mobile
            profile.mobile_verified = True
            profile.mobile_verified_at = datetime.now(timezone.utc)
            profile.core_user_id = UUID(core_result["user_id"])
            if data.display_name:
                profile.display_name = data.display_name

        await self.session.commit()
        await self.session.refresh(profile)
        await self._sync_platform_admin_role(profile)

        return await self.auth.issue_session_for_profile(
            profile,
            message="ثبت‌نام و ورود با موفقیت انجام شد",
            intent="register",
            password=password if has_credentials else None,
            sync_mobile_password=not has_credentials,
        )

    async def _sync_platform_admin_role(self, profile: UserProfile) -> None:
        if not profile.mobile or profile.mobile not in settings.platform_admin_mobile_set:
            return
        await self.keycloak.assign_realm_role(profile.keycloak_sub, "platform_admin")
