"""ثبت‌نام یکپارچه SSO + تأیید موبایل."""
from __future__ import annotations

from datetime import datetime, timezone
from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.models.user import UserProfile
from app.repositories.user import UserRepository
from app.schemas.auth import (
    AuthSessionResponse,
    MobileOtpRequest,
    MobileOtpVerify,
    MobileRegisterVerify,
    OtpRequestResponse,
    RegisterCompleteResponse,
    RegisterRequest,
    VerifyMobileResponse,
)
from app.services.auth_service import AuthService
from app.services.keycloak_client import KeycloakAdminClient, mobile_keycloak_password
from app.services.otp_client import CoreOtpClient, OtpProxyError
from shared.exceptions import ConflictError, UnauthorizedError
from shared.security import CurrentUser


class RegistrationService:
    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 register_with_credentials(self, data: RegisterRequest) -> OtpRequestResponse:
        if await self.users.get_by_mobile(data.mobile):
            raise ConflictError("این شماره موبایل قبلاً ثبت شده است", error_code="mobile_taken")
        if await self.users.get_by_username(data.username):
            raise ConflictError("نام کاربری تکراری است", error_code="username_taken")

        sub = await self.keycloak.create_user(
            email=data.email,
            username=data.username,
            password=data.password,
            display_name=data.display_name,
            mobile=data.mobile,
        )
        if await self.users.get_by_sub(sub):
            raise ConflictError("کاربر از قبل وجود دارد", error_code="user_exists")

        profile = UserProfile(
            keycloak_sub=sub,
            email=data.email,
            username=data.username,
            display_name=data.display_name,
            mobile=data.mobile,
            mobile_verified=False,
        )
        await self.users.add(profile)
        await self.session.commit()

        result = await self.otp.request_otp(data.mobile)
        return OtpRequestResponse(
            message="حساب ایجاد شد. کد تأیید موبایل ارسال شد",
            expires_in=result.get("expires_in", 120),
            skipped=result.get("skipped", False),
        )

    async def complete_registration_verify(self, data: MobileOtpVerify) -> AuthSessionResponse:
        profile = await self.users.get_by_mobile(data.mobile)
        if profile is None:
            raise UnauthorizedError("ثبت‌نام یافت نشد. ابتدا فرم ثبت‌نام را تکمیل کنید")
        if profile.mobile_verified:
            return await self.auth.issue_session_for_profile(
                profile,
                message="شماره موبایل قبلاً تأیید شده — وارد شدید",
                intent="login",
            )

        core_result = await self.otp.verify_otp(
            data.mobile,
            data.code,
            keycloak_sub=profile.keycloak_sub,
            email=profile.email,
        )
        profile.mobile_verified = True
        profile.mobile_verified_at = datetime.now(timezone.utc)
        profile.core_user_id = UUID(core_result["user_id"])
        await self.session.commit()
        await self.session.refresh(profile)

        return await self.auth.issue_session_for_profile(
            profile,
            message="ثبت‌نام با موفقیت تکمیل شد",
            intent="register",
        )

    async def request_mobile_register_otp(self, data: MobileOtpRequest) -> OtpRequestResponse:
        existing = await self.users.get_by_mobile(data.mobile)
        if existing and existing.mobile_verified:
            raise ConflictError(
                "این شماره قبلاً ثبت شده است. از ورود با موبایل استفاده کنید",
                error_code="mobile_exists_use_login",
            )
        result = await self.otp.request_otp(data.mobile, force_sms=data.force_sms)
        return OtpRequestResponse(
            message=result.get("message", "کد تأیید ارسال شد"),
            expires_in=result.get("expires_in", 120),
            skipped=result.get("skipped", False),
        )

    async def complete_mobile_register(self, data: MobileRegisterVerify) -> AuthSessionResponse:
        profile = await self.users.get_by_mobile(data.mobile)
        if profile and profile.mobile_verified:
            raise ConflictError(
                "این شماره قبلاً ثبت شده است. از ورود با موبایل استفاده کنید",
                error_code="mobile_exists_use_login",
            )

        username = data.mobile
        email = f"{data.mobile[1:]}@mobile.torbatyar.local"
        temp_password = mobile_keycloak_password(data.mobile)

        sub = await self.keycloak.create_user(
            email=email,
            username=username,
            password=temp_password,
            display_name=data.display_name or data.mobile,
            mobile=data.mobile,
        )

        core_result = await self.otp.verify_otp(
            data.mobile,
            data.code,
            email=email,
            keycloak_sub=sub,
        )

        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,
                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.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)

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

    async def request_verify_mobile(self, current: CurrentUser, mobile: str) -> OtpRequestResponse:
        profile = await self.users.get_by_sub(current.user_id)
        if profile is None:
            raise UnauthorizedError("پروفایل کاربر یافت نشد")
        if profile.mobile_verified and profile.mobile == mobile:
            return OtpRequestResponse(message="شماره موبایل قبلاً تأیید شده", expires_in=0, skipped=True)

        other = await self.users.get_by_mobile(mobile)
        if other and other.keycloak_sub != current.user_id:
            raise ConflictError("این شماره موبایل به کاربر دیگری تعلق دارد", error_code="mobile_taken")

        profile.mobile = mobile
        profile.mobile_verified = False
        await self.session.commit()

        result = await self.otp.request_otp(mobile)
        return OtpRequestResponse(
            message=result.get("message", "کد تأیید ارسال شد"),
            expires_in=result.get("expires_in", 120),
            skipped=result.get("skipped", False),
        )

    async def verify_mobile_for_user(
        self, current: CurrentUser, data: MobileOtpVerify
    ) -> VerifyMobileResponse:
        profile = await self.users.get_by_sub(current.user_id)
        if profile is None:
            raise UnauthorizedError("پروفایل کاربر یافت نشد")
        if profile.mobile and profile.mobile != data.mobile:
            raise ConflictError("شماره موبایل با درخواست قبلی مطابقت ندارد")

        core_result = await self.otp.verify_otp(
            data.mobile,
            data.code,
            keycloak_sub=profile.keycloak_sub,
            email=profile.email,
        )
        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"])
        await self.session.commit()

        return VerifyMobileResponse(
            message="شماره موبایل تأیید شد",
            mobile_verified=True,
            mobile_verified_at=profile.mobile_verified_at,
        )
