"""Пользователи: создание из внешней личности, ник, активная группа.""" from __future__ import annotations import re from sqlmodel import Session, select from app.auth.provider import ExternalIdentity from app.core.errors import NicknameTakenError, NotFoundError, ValidationError from app.models import AuthIdentity, GroupMember, User _NICK_RE = re.compile(r"^[\w .\-]{2,64}$", re.UNICODE) def get_user(session: Session, user_id: int) -> User: user = session.get(User, user_id) if user is None: raise NotFoundError("Пользователь не найден.") return user def nickname_available(session: Session, nickname: str, exclude_user_id: int | None = None) -> bool: stmt = select(User).where(User.nickname == nickname) existing = session.exec(stmt).first() return existing is None or existing.id == exclude_user_id def _unique_nickname(session: Session, base: str) -> str: base = (base or "Игрок").strip()[:60] or "Игрок" candidate = base suffix = 1 while session.exec(select(User).where(User.nickname == candidate)).first() is not None: suffix += 1 candidate = f"{base} {suffix}" return candidate def get_or_create_from_identity(session: Session, identity: ExternalIdentity) -> User: link = session.exec( select(AuthIdentity).where( AuthIdentity.provider == identity.provider, AuthIdentity.external_id == identity.external_id, ) ).first() if link is not None: return get_user(session, link.user_id) user = User( nickname=_unique_nickname(session, identity.suggested_nickname or identity.external_id), role="player", auth_provider=identity.provider, telegram_id=identity.telegram_id, ) session.add(user) session.flush() session.add( AuthIdentity( user_id=user.id, # type: ignore[arg-type] provider=identity.provider, external_id=identity.external_id, ) ) session.commit() session.refresh(user) return user def update_nickname(session: Session, user: User, new_nickname: str) -> User: new_nickname = (new_nickname or "").strip() if not _NICK_RE.match(new_nickname): raise ValidationError("Ник: 2–64 символа, буквы/цифры/пробел/.-_") if not nickname_available(session, new_nickname, exclude_user_id=user.id): raise NicknameTakenError() user.nickname = new_nickname session.add(user) session.commit() session.refresh(user) return user def set_active_group(session: Session, user: User, group_id: int | None) -> User: if group_id is not None: member = session.exec( select(GroupMember).where( GroupMember.group_id == group_id, GroupMember.user_id == user.id ) ).first() if member is None: raise ValidationError("Нельзя сделать активной группу, в которой вы не состоите.") user.active_group_id = group_id session.add(user) session.commit() session.refresh(user) return user