from typing import Optional

import pytest

from time_manager.db.exceptions import IntegrityError, ItemNotFound
from time_manager.db.sql.base import session_scope
from time_manager.db.sql.models.user import User
from time_manager.db.sql.user import UserDao
from time_manager.schemas.user import UserDBBase


@pytest.mark.parametrize(
    "username, password, email",
    [
        ("user", "password", "<PASSWORD>"),
    ],
)
def test_user_dao_post(username, password, email, dao: UserDao):
    with dao.post(
        item=UserDBBase(username=username, hashed_password=password, email=email)
    ) as user:
        assert user.id is not None
        assert user.username == username
        assert user.email == email
        assert user.hashed_password == password

    with pytest.raises(IntegrityError):
        with dao.post(
            item=UserDBBase(username=username, hashed_password=password, email=email)
        ) as user:
            ...

    with pytest.raises(IntegrityError):
        with dao.post(
            item=UserDBBase(
                username=username + "1", hashed_password=password, email=email
            )
        ) as user:
            ...

    with dao.post(
        item=UserDBBase(
            username=username + "1", hashed_password=password, email=email + "m"
        )
    ) as user:
        assert user.id is not None
        assert user.username == username + "1"
        assert user.hashed_password == password
        assert user.email == email + "m"


@pytest.mark.parametrize(
    "data",
    [
        [
            ("user1", "password1", "<PASSWORD>"),
            ("user2", "password2", "<PASSWORD>.a"),
        ]
    ],
)
def test_user_dao_filter(data, dao: UserDao):
    with session_scope(dao.session) as db:
        for username, password, email in data:
            db.add(User(username=username, hashed_password=password, email=email))

    items = []
    for i in dao.filter():
        items.append(UserDBBase.from_orm(i))

    assert len(items) == len(data)
    for (username, password, email), user in zip(data, items):
        assert user.id is not None
        assert user.username == username
        assert user.email == email
        assert user.hashed_password == password

    items = []
    for i in dao.filter(skip=1):
        items.append(UserDBBase.from_orm(i))

    assert len(items) == len(data) - 1
    for (username, password, email), user in zip(data[1:], items):
        assert user.id is not None
        assert user.username == username
        assert user.email == email
        assert user.hashed_password == password

    items = []
    for i in dao.filter(limit=1):
        items.append(UserDBBase.from_orm(i))

    assert len(items) == len(data) - 1
    for (username, password, email), user in zip(data[:1], items):
        assert user.id is not None
        assert user.username == username
        assert user.email == email
        assert user.hashed_password == password

    items = []
    for i in dao.filter(username="1"):
        items.append(UserDBBase.from_orm(i))

    assert len(items) == 0

    items = []
    for i in dao.filter(username=data[0][0]):
        items.append(UserDBBase.from_orm(i))

    assert len(items) == 1
    assert items[0].hashed_password == data[0][1]


@pytest.mark.parametrize(
    "data",
    [
        [
            ("user1", "<PASSWORD>", "<PASSWORD>"),
            ("user2", "password2", "<PASSWORD>"),
        ]
    ],
)
def test_user_dao_delete(data, dao: UserDao):
    with session_scope(dao.session) as db:
        for username, password, email in data:
            db.add(User(username=username, hashed_password=password, email=email))

    ids = [i.id for i in dao.filter()]

    for (index, id) in enumerate(ids):
        assert id is not None
        with dao.delete(id):
            ...
        assert len(list(dao.filter())) == len(data) - index - 1

    assert len(list(dao.filter())) == 0
    with dao.delete(1) as user:
        assert user is None


@pytest.mark.parametrize(
    "data",
    [
        ("user2", "<PASSWORD>", "<PASSWORD>.a"),
    ],
)
def test_user_dao_put(data, dao: UserDao):
    with session_scope(dao.session) as db:
        db.add(User(username=data[0], hashed_password=data[1], email=data[2]))

    item: Optional[UserDBBase] = None
    for i in dao.filter():
        item = UserDBBase.from_orm(i)
        break

    assert item

    assert item.id is not None

    with pytest.raises(ItemNotFound):
        with dao.put(
            item.id + 1,
            UserDBBase(username=f"*{item.username}*", full_name="full_name"),
        ):
            ...

    with dao.put(
        item.id,
        UserDBBase(username=f"*{item.username}*", full_name="full_name"),
        partial=True,
    ) as result:
        assert result.username == f"*{item.username}*"
        assert result.full_name == "full_name"

    with dao.put(
        item.id,
        UserDBBase(username="notpartial", hashed_password="", email="<EMAIL>"),
        partial=False,
    ) as result:
        assert result.username == "notpartial"
        assert result.email == "<EMAIL>"
        assert result.full_name is None

from typing import Optional

import pytest

from time_manager.db.exceptions import IntegrityError, ItemNotFound
from time_manager.db.sql.base import session_scope
from time_manager.db.sql.models.user import User
from time_manager.db.sql.user import UserDao
from time_manager.schemas.user import UserDBBase


@pytest.mark.parametrize(
    "username, password, email",
    [
        ("user", "password", "<PASSWORD>"),
    ],
)
def test_user_dao_post(username, password, email, dao: UserDao):
    with dao.post(
        item=UserDBBase(username=username, hashed_password=password, email=email)
    ) as user:
        assert user.id is not None
        assert user.username == username
        assert user.email == email
        assert user.hashed_password == password

    with pytest.raises(IntegrityError):
        with dao.post(
            item=UserDBBase(username=username, hashed_password=password, email=email)
        ) as user:
            ...

    with pytest.raises(IntegrityError):
        with dao.post(
            item=UserDBBase(
                username=username + "1", hashed_password=password, email=email
            )
        ) as user:
            ...

    with dao.post(
        item=UserDBBase(
            username=username + "1", hashed_password=password, email=email + "m"
        )
    ) as user:
        assert user.id is not None
        assert user.username == username + "1"
        assert user.hashed_password == password
        assert user.email == email + "m"


@pytest.mark.parametrize(
    "data",
    [
        [
            ("user1", "password1", "<PASSWORD>"),
            ("user2", "password2", "<PASSWORD>.a"),
        ]
    ],
)
def test_user_dao_filter(data, dao: UserDao):
    with session_scope(dao.session) as db:
        for username, password, email in data:
            db.add(User(username=username, hashed_password=password, email=email))

    items = []
    for i in dao.filter():
        items.append(UserDBBase.from_orm(i))

    assert len(items) == len(data)
    for (username, password, email), user in zip(data, items):
        assert user.id is not None
        assert user.username == username
        assert user.email == email
        assert user.hashed_password == password

    items = []
    for i in dao.filter(skip=1):
        items.append(UserDBBase.from_orm(i))

    assert len(items) == len(data) - 1
    for (username, password, email), user in zip(data[1:], items):
        assert user.id is not None
        assert user.username == username
        assert user.email == email
        assert user.hashed_password == password

    items = []
    for i in dao.filter(limit=1):
        items.append(UserDBBase.from_orm(i))

    assert len(items) == len(data) - 1
    for (username, password, email), user in zip(data[:1], items):
        assert user.id is not None
        assert user.username == username
        assert user.email == email
        assert user.hashed_password == password

    items = []
    for i in dao.filter(username="1"):
        items.append(UserDBBase.from_orm(i))

    assert len(items) == 0

    items = []
    for i in dao.filter(username=data[0][0]):
        items.append(UserDBBase.from_orm(i))

    assert len(items) == 1
    assert items[0].hashed_password == data[0][1]


@pytest.mark.parametrize(
    "data",
    [
        [
            ("user1", "<PASSWORD>", "<PASSWORD>"),
            ("user2", "password2", "<PASSWORD>"),
        ]
    ],
)
def test_user_dao_delete(data, dao: UserDao):
    with session_scope(dao.session) as db:
        for username, password, email in data:
            db.add(User(username=username, hashed_password=password, email=email))

    ids = [i.id for i in dao.filter()]

    for (index, id) in enumerate(ids):
        assert id is not None
        with dao.delete(id):
            ...
        assert len(list(dao.filter())) == len(data) - index - 1

    assert len(list(dao.filter())) == 0
    with dao.delete(1) as user:
        assert user is None


@pytest.mark.parametrize(
    "data",
    [
        ("user2", "<PASSWORD>", "<PASSWORD>.a"),
    ],
)
def test_user_dao_put(data, dao: UserDao):
    with session_scope(dao.session) as db:
        db.add(User(username=data[0], hashed_password=data[1], email=data[2]))

    item: Optional[UserDBBase] = None
    for i in dao.filter():
        item = UserDBBase.from_orm(i)
        break

    assert item

    assert item.id is not None

    with pytest.raises(ItemNotFound):
        with dao.put(
            item.id + 1,
            UserDBBase(username=f"*{item.username}*", full_name="full_name"),
        ):
            ...

    with dao.put(
        item.id,
        UserDBBase(username=f"*{item.username}*", full_name="full_name"),
        partial=True,
    ) as result:
        assert result.username == f"*{item.username}*"
        assert result.full_name == "full_name"

    with dao.put(
        item.id,
        UserDBBase(username="notpartial", hashed_password="", email="<EMAIL>"),
        partial=False,
    ) as result:
        assert result.username == "notpartial"
        assert result.email == "<EMAIL>"
        assert result.full_name is None
