# app/crud/user.py
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from sqlalchemy.exc import SQLAlchemyError, IntegrityError, OperationalError
from typing import Optional

from app.models import User as UserModel # Alias to avoid name clash
from app.schemas.user import UserCreate
from app.core.security import hash_password
from app.core.exceptions import (
    UserCreationError,
    EmailAlreadyRegisteredError,
    DatabaseConnectionError,
    DatabaseIntegrityError,
    DatabaseQueryError,
    DatabaseTransactionError
)

async def get_user_by_email(db: AsyncSession, email: str) -> Optional[UserModel]:
    """Fetches a user from the database by email."""
    try:
        async with db.begin():
            result = await db.execute(select(UserModel).filter(UserModel.email == email))
            return result.scalars().first()
    except OperationalError as e:
        raise DatabaseConnectionError(f"Failed to connect to database: {str(e)}")
    except SQLAlchemyError as e:
        raise DatabaseQueryError(f"Failed to query user: {str(e)}")

async def create_user(db: AsyncSession, user_in: UserCreate) -> UserModel:
    """Creates a new user record in the database."""
    try:
        async with db.begin():
            _hashed_password = hash_password(user_in.password)
            db_user = UserModel(
                email=user_in.email,
                password_hash=_hashed_password,
                name=user_in.name
            )
            db.add(db_user)
            await db.flush()  # Flush to get DB-generated values
            await db.refresh(db_user)
            return db_user
    except IntegrityError as e:
        if "unique constraint" in str(e).lower():
            raise EmailAlreadyRegisteredError()
        raise DatabaseIntegrityError(f"Failed to create user: {str(e)}")
    except OperationalError as e:
        raise DatabaseConnectionError(f"Database connection error: {str(e)}")
    except SQLAlchemyError as e:
        raise DatabaseTransactionError(f"Failed to create user: {str(e)}")