from copy import deepcopy
import models
from psql_database import engine, Base, SessionLocal
import sqlalchemy as sa
import schemas as schemas
import passlib.hash as pwd_hash
import re
from fastapi import HTTPException, Depends, security
import jwt


OAUTH2_SCHEME = security.OAuth2PasswordBearer(tokenUrl="/api/token")

JWT_SECRET = 'myjwtsecret'


def validate_email(email):
    pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
    return bool(re.match(pattern, email))


def create_database_tables():
    return Base.metadata.create_all(bind=engine)


def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


async def get_user_by_email(email: str, db: sa.orm.Session):
    return db.query(models.User).filter(models.User.email == email).first()


async def create_user(user: schemas.userCreate, db: sa.orm.Session):

    if not validate_email(user.email):
        raise HTTPException(status_code=400, detail="Invalid email")

    user_obj = models.User(
        first_name=user.first_name,
        last_name=user.last_name,
        email=user.email,
        password=pwd_hash.bcrypt.hash(user.password)
    )

    db.add(user_obj)
    db.commit()  # commit operation
    db.refresh(user_obj)  # Await the refresh operation
    return user_obj


async def authenticate_user(email, password, db: sa.orm.Session):
    user = await get_user_by_email(email=email, db=db)

    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user if user.verify_password(password) else False


async def create_token(user: models.User):
    user_dict = {
        "first_name": user.first_name,
        "last_name": user.last_name,
        "email": user.email,
    }
    token = jwt.encode(user_dict, JWT_SECRET)

    return dict(access_token=token, token_type='bearer', status='success')


async def get_current_user(db: sa.orm.Session = Depends(get_db), token: str = Depends(OAUTH2_SCHEME)):
    try:
        payload = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])
        print(payload)
        user = db.query(models.User).filter(
            models.User.email == payload['email']).first()
        if not user:
            raise HTTPException(status_code=404, detail="User not found")
        return schemas.User(**user.__dict__)
    except Exception as e:
        raise HTTPException(status_code=401, detail="Invalid token") from e


async def create_todo(user: schemas.User, todo: schemas.TodoCreate, db: sa.orm.Session = Depends(get_db)):
    print(todo.dict())

    todo_obj = models.Todo(
        task_name=todo.task_name,
        task_description=todo.task_description,
        priority=todo.priority,
        category=todo.category,
        due_date=todo.due_date,
        status=todo.status,
        user_id=user.user_id,
    )

    db.add(todo_obj)
    db.commit()
    db.refresh(todo_obj)
    return todo_obj


async def get_todos(user: schemas.User, db: sa.orm.Session = Depends(get_db)):
    return db.query(models.Todo).filter(models.Todo.user_id == user.user_id).all()


async def delete_todo(todo_id: int, user: schemas.User, db: sa.orm.Session = Depends(get_db)):
    todo = db.query(models.Todo).filter(models.Todo.user_id ==
                                        user.user_id, models.Todo.todo_id == todo_id).first()
    if not todo:
        raise HTTPException(status_code=404, detail="Todo not found")
    db.delete(todo)
    db.commit()
    return todo


async def update_todo(todo_id: int, user: schemas.User, todo: schemas.TodoCreate, db: sa.orm.Session = Depends(get_db)):
    todo_db = db.query(models.Todo).filter(
        models.Todo.user_id == user.user_id, models.Todo.todo_id == todo_id).first()
    if not todo_db:
        raise HTTPException(status_code=404, detail="Todo not found")
    todo_db.task_name = todo.task_name
    todo_db.task_description = todo.task_description or todo.task_description
    todo_db.priority = todo.priority
    todo_db.category = todo.category
    todo_db.due_date = todo.due_date
    todo_db.status = todo.status

    db.commit()
    db.refresh(todo_db)
    return todo_db


async def get_today_todos(user: schemas.User, db: sa.orm.Session = Depends(get_db)):
    return db.query(models.Todo).filter(models.Todo.user_id == user.user_id, sa.func.date(models.Todo.due_date) == sa.func.date(sa.func.now())).all()


async def get_overdue_todos(user: schemas.User, db: sa.orm.Session = Depends(get_db)):
    return db.query(models.Todo).filter(models.Todo.user_id == user.user_id, models.Todo.due_date < sa.func.now()).all()


async def get_upcoming_todos(user: schemas.User, db: sa.orm.Session = Depends(get_db)):
    return db.query(models.Todo).filter(models.Todo.user_id == user.user_id, models.Todo.due_date > sa.func.now()).all()


async def get_completed_todos(user: schemas.User, db: sa.orm.Session = Depends(get_db)):
    return db.query(models.Todo).filter(models.Todo.user_id == user.user_id, models.Todo.status == True).all()