"""This module contains utilities for creating fake database entries."""

from random import randint

from faker import Faker
from sqlalchemy.orm import Session

from todo.schemas.users import UserCreate, User
from todo.auth.auth import AuthHandler
from todo.models.users import User as UserModel
from todo.schemas.todos import TodoItemCreate, TodoItem
from todo.models.todos import TodoItem as TodoItemModel
from todo.database.engine import get_db


auth_handler = AuthHandler()


def _get_faker() -> Faker:
    """Creates and returns a Faker object."""

    return Faker()


def get_fake_user_details() -> UserCreate:
    """Returns a set of fake details for creating a new user."""

    fk = _get_faker()

    email = fk.profile(fields=['mail'])['mail']
    first_name = fk.first_name()
    last_name = fk.last_name()
    password = email

    return UserCreate(
        email=email,
        first_name=first_name,
        last_name=last_name,
        password=password,
        password_confirmation=password,
        is_admin=False,
    )


def get_fake_todo_details() -> TodoItemCreate:
    """Returns a set of fake details for creating a new todo-item."""

    fk = _get_faker()

    title = fk.sentence(nb_words=randint(4, 6))

    # Generate some sentences and concatenate them with a randomized delimiter
    description_sentences = [fk.sentence(nb_words=randint(4, 6)) for i in range(randint(1, 5))]
    if randint(0, 1):
        delimiter = '\n- '
        description_sentences[0] = "- " + description_sentences[0]
    elif randint(0, 1):
        delimiter = '\n'
    else:
        delimiter = ' '
    description = delimiter.join(description_sentences)

    return TodoItemCreate(
        title=title,
        description=description,
    )


def create_fake_user(db: Session = next(get_db())) -> User:
    """Creates a fake user and saves them to the database."""

    user = get_fake_user_details()

    fk = _get_faker()
    created = fk.date_time_between('-2y', 'now')
    updated = fk.date_time_between(created, 'now')
    is_admin = randint(0, 10) == 0

    db_user = UserModel(
        email=user.email,
        first_name=user.first_name,
        last_name=user.last_name,
        password=auth_handler.hash_password(user.password),
        created=created,
        updated=updated,
        is_admin=is_admin,
    )

    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return User.from_orm(db_user)


def create_fake_todo(user_id: int, db: Session = next(get_db())) -> TodoItem:
    """Creates a fake todo-item for the specified user and saves it to the database."""

    todo = get_fake_todo_details()

    db_user = db.query(UserModel).filter(UserModel.id == user_id).first()
    if not db_user:
        raise RuntimeError('User not found.')

    fk = _get_faker()
    created = fk.date_time_between(db_user.created, 'now')
    if randint(1, 4) == 1:
        updated = fk.date_time_between(created, 'now')
    else:
        updated = created

    if randint(1, 4) == 1:
        finished = fk.date_time_between(created, 'now')
        updated = finished
        done = True
    else:
        finished = None
        done = False


    db_todo = TodoItemModel(
        user_id=user_id,
        title=todo.title,
        description=todo.description,
        done=done,
        created=created,
        updated=updated,
        finished=finished
    )

    db.add(db_todo)
    db.commit()
    db.refresh(db_todo)
    return TodoItem.from_orm(db_todo)


def populate_database(num_users: int = randint(50, 150), max_todos_per_user: int = randint(50, 150)) -> None:
    """Creates the specified number of users, each with between 0 and max_todos_per_user todo-items."""

    db = next(get_db())

    for i in range(num_users):
        user = create_fake_user(db)
        for j in range(randint(0, max_todos_per_user)):
            create_fake_todo(user.id, db)
