import typing

import sqlalchemy
from sqlalchemy.sql import functions as sqlalchemy_functions

from app.core.database.models.user import User
from app.apis.apiV1.Repository.BaseRepository import BaseCRUDRepository
#from src.securities.hashing.password import pwd_generator
##from src.securities.verifications.credentials import credential_verifier
from app.utilities.exceptions.database import EntityAlreadyExists, EntityDoesNotExist
from app.utilities.exceptions.password import PasswordDoesNotMatch
from app.core.auth import get_password_hash
from app.apis.apiV1.Resourses.User.UserResourses import UserInCreate
from app.core.database.database import async_db
from app.apis.apiV1.Dependencies.session import get_async_session
from app.core.database.connections import get_db
from sqlalchemy.orm import Session
from fastapi import  Depends 
from app.core.verifications.credentials import credential_verifier


class UserRepository(BaseCRUDRepository):
    
    def store(user_create: UserInCreate ,session: Session) -> User:
    
        new_user = User(username=user_create.username, email=user_create.email, is_logged_in=True
                        , hashed_password=get_password_hash(password=user_create.password )
        , is_verified=True
        )
        session.add(new_user)
        session.commit()
        session.refresh(new_user)
        return new_user
    def getAll(session: Session,skip: int = 0, limit: int = 100):
        return session.query(User).offset(skip).limit(limit).all()
        
    def getByEmail(session: Session,email):
        user = session.query(User).filter(User.email == email).first()
        return user
    

    """
    async def read_users(self) -> typing.Sequence[user]:
        stmt = sqlalchemy.select(user)
        query = await self.async_session.execute(statement=stmt)
        return query.scalars().all()

    async def read_user_by_id(self, id: int) -> user:
        stmt = sqlalchemy.select(user).where(user.id == id)
        query = await self.async_session.execute(statement=stmt)

        if not query:
            raise EntityDoesNotExist("user with id `{id}` does not exist!")

        return query.scalar()  # type: ignore

    async def read_user_by_username(self, username: str) -> user:
        stmt = sqlalchemy.select(user).where(user.username == username)
        query = await self.async_session.execute(statement=stmt)

        if not query:
            raise EntityDoesNotExist("user with username `{username}` does not exist!")

        return query.scalar()  # type: ignore

    async def read_user_by_email(self, email: str) -> user:
        stmt = sqlalchemy.select(user).where(user.email == email)
        query = await self.async_session.execute(statement=stmt)

        if not query:
            raise EntityDoesNotExist("user with email `{email}` does not exist!")

        return query.scalar()  # type: ignore

    async def read_user_by_password_authentication(self, user_login: userInLogin) -> user:
        stmt = sqlalchemy.select(user).where(
            user.username == user_login.username, user.email == user_login.email
        )
        query = await self.async_session.execute(statement=stmt)
        db_user = query.scalar()

        if not db_user:
            raise EntityDoesNotExist("Wrong username or wrong email!")

        if not pwd_generator.is_password_authenticated(hash_salt=db_user.hash_salt, password=user_login.password, hashed_password=db_user.hashed_password):  # type: ignore
            raise PasswordDoesNotMatch("Password does not match!")

        return db_user  # type: ignore

    async def update_user_by_id(self, id: int, user_update: userInUpdate) -> user:
        new_user_data = user_update.dict()

        select_stmt = sqlalchemy.select(user).where(user.id == id)
        query = await self.async_session.execute(statement=select_stmt)
        update_user = query.scalar()

        if not update_user:
            raise EntityDoesNotExist(f"user with id `{id}` does not exist!")  # type: ignore

        update_stmt = sqlalchemy.update(table=user).where(user.id == update_user.id).values(updated_at=sqlalchemy_functions.now())  # type: ignore

        if new_user_data["username"]:
            update_stmt = update_stmt.values(username=new_user_data["username"])

        if new_user_data["email"]:
            update_stmt = update_stmt.values(username=new_user_data["email"])

        if new_user_data["password"]:
            update_user.set_hash_salt(hash_salt=pwd_generator.generate_salt)  # type: ignore
            update_user.set_hashed_password(hashed_password=pwd_generator.generate_hashed_password(hash_salt=update_user.hash_salt, new_password=new_user_data["password"]))  # type: ignore

        await self.async_session.execute(statement=update_stmt)
        await self.async_session.commit()
        await self.async_session.refresh(instance=update_user)

        return update_user  # type: ignore

    async def delete_user_by_id(self, id: int) -> str:
        select_stmt = sqlalchemy.select(user).where(user.id == id)
        query = await self.async_session.execute(statement=select_stmt)
        delete_user = query.scalar()

        if not delete_user:
            raise EntityDoesNotExist(f"user with id `{id}` does not exist!")  # type: ignore

        stmt = sqlalchemy.delete(table=user).where(user.id == delete_user.id)

        await self.async_session.execute(statement=stmt)
        await self.async_session.commit()

        return f"user with id '{id}' is successfully deleted!"
    """
    def is_username_taken(username: str,session: Session) -> bool:
        username_stmt = sqlalchemy.select(User.username).select_from(User).where(User.username == username)
        username_query = session.execute(username_stmt)
        db_username = username_query.scalar()

        if not credential_verifier.is_username_available(username=db_username):
            raise EntityAlreadyExists(f"The username `{username}` is already taken!")  # type: ignore

        return True

    def is_email_taken(email: str,session: Session) -> bool:
        email_stmt = sqlalchemy.select(User.email).select_from(User).where(User.email == email)
        email_query = session.execute(email_stmt)
        db_email = email_query.scalar()

        if not credential_verifier.is_email_available(email=db_email):
            raise EntityAlreadyExists(f"The email `{email}` is already registered!")  # type: ignore

        return True
   
