"""
Tests all db utilities such as CRUDMixin defined under shopapi/models.py
Most of the test cases taken from:
https://github.com/cookiecutter-flask/cookiecutter-flask
"""

import pytest
from flask_login import UserMixin
from shopyoapi.init import db
from shopyoapi.models import PkModel


class ExampleUserModel(PkModel, UserMixin):
    """Example user model for testing purposes"""

    __tablename__ = "testusers"

    username = db.Column(db.String(100), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)


class TestPKModel:
    """Tests all functions of PKModel"""

    def test_get_by_id(self):
        result = ExampleUserModel.get_by_id("myId")

        assert not result

    def test_get_by_id_valid(self, db_session):
        user_bar = ExampleUserModel(username="bar", email="bar@domain.com")
        user_foo = ExampleUserModel(username="foo", email="foo@domain.com")
        user_bar.save()
        user_foo.save()
        result_int = ExampleUserModel.get_by_id(user_bar.id)
        result_str = ExampleUserModel.get_by_id(str(user_bar.id))
        expected = (
            db_session.query(ExampleUserModel)
            .filter(ExampleUserModel.username == "bar")
            .scalar()
        )

        assert result_int
        assert expected
        assert result_int.username == expected.username
        assert result_str.username == expected.username


class TestCRUDMixin:
    """Test class for testing all CRUD functions"""

    def test_create(self, db_session):
        user = ExampleUserModel.create(username="bar", email="bar@domain.com")
        result_raw = db_session.execute(
            """select * from testusers"""
        ).fetchone()
        result_orm = (
            db_session.query(ExampleUserModel)
            .filter(ExampleUserModel.id == user.id)
            .scalar()
        )

        assert result_orm
        assert result_raw
        assert result_raw.username == "bar"
        assert result_orm.username == "bar"

    @pytest.mark.parametrize(
        "commit,expected", [(True, "foo"), (False, "bar")]
    )
    def test_update_single(self, db_session, commit, expected):
        user = ExampleUserModel(username="bar", email="bar@domain.com")
        user.save()
        user.update(commit=commit, username="foo")
        result = db_session.execute("""select * from testusers""").fetchone()

        assert result
        assert result.username == expected

    @pytest.mark.parametrize(
        "commit,expected",
        [
            (True, {"username": "foo", "email": "foo@domain.com"}),
            (False, {"username": "bar", "email": "bar@domain.com"}),
        ],
    )
    def test_update_multiple(self, db_session, commit, expected):
        user = ExampleUserModel(username="bar", email="bar@domain.com")
        user.save()
        user.update(commit=commit, username="foo", email="foo@domain.com")
        result = db_session.execute("""select * from testusers""").fetchone()

        assert result
        assert result.username == expected["username"]
        assert result.email == expected["email"]

    @pytest.mark.parametrize("commit,expected", [(True, None), (False, "bar")])
    def test_delete(self, commit, expected):
        user = ExampleUserModel(username="bar", email="bar@domain.com")
        user.save()
        user.delete(commit=commit)
        result = ExampleUserModel.get_by_id(user.id)
        if result:
            result = result.username

        assert result == expected
