from psycopg2 import DataError, ProgrammingError
import pytest
import datetime
from drunken_boat.db.exceptions import NotFoundError

from drunken_boat.db.postgresql.tests import projections_fixtures
from drunken_boat.db.postgresql.projections import DataBaseObject
from drunken_boat.db.postgresql.query import Query, Where
from drunken_boat.db.postgresql import DB
from drunken_boat.db.postgresql.tests import get_test_db


@pytest.fixture(scope="module")
def prepare_test():
    return projections_fixtures.prepare_test()


def test_db(prepare_test):
    db = prepare_test
    assert db.select("SELECT id, title, introduction FROM dummy LIMIT 1") == [
        (1, 'hello', "This is an introduction")]


def test_projection_select(prepare_test):
    projection = projections_fixtures.TestProjection(DB(database="dummy_db"))
    results = projection.select()
    assert isinstance(results[0], DataBaseObject)
    assert results[0].title == "hello"
    assert results[1].title == "goodbye"
    results = projection.select(lazy=True)
    assert isinstance(results, Query)
    where = Where("title", "=", "%s")
    results = projection.select(where=where, params=("hello",))
    assert results[0].title == "hello"


def test_projection_get_by_py(prepare_test):
    projection = projections_fixtures.TestProjection(DB(database="dummy_db"))
    assert isinstance(projection.get_by_pk(1), DataBaseObject)
    assert isinstance(projection.get_by_pk(2), DataBaseObject)


def test_raising_projection(prepare_test):
    pytest.raises(ValueError,
                  projections_fixtures.TestRaiseProjection, get_test_db())
    projection = projections_fixtures.TestRaiseProjectionWithNoGetTable(
        get_test_db())
    pytest.raises(ValueError, projection.select)


def test_projection_query(prepare_test):
    projection = projections_fixtures.TestProjection(get_test_db())
    query = "select title from dummy"
    assert isinstance(projection.select(query=query)[0], DataBaseObject)


def test_projection_query_where(prepare_test):
    projection = projections_fixtures.TestProjection(get_test_db())
    where = Where("title", "=", "%s")
    projection = projection.select(where=where, params=("hello",))
    assert projection[0].title == "hello"


def test_projection_query_where_or(prepare_test):
    projection = projections_fixtures.TestProjection(get_test_db())
    where = Where("title", "=", "%s") | Where("id", "=", "%s")
    results = projection.select(where=where, params=("hello", 2))
    assert results[0].title == "hello"
    assert results[1].title == "goodbye"
    results = projection.select(where=where, params=("hello", 1))
    assert len(results) == 1


def test_projection_query_where_and(prepare_test):
    projection = projections_fixtures.TestProjection(get_test_db())
    where = Where("title", "=", "%s") & Where("id", "=", "%s")
    results = projection.select(where=where, params=("hello", 2))
    assert len(results) == 0
    results = projection.select(where=where, params=("hello", 1))
    assert len(results) == 1


def test_projection_query_where_nor(prepare_test):
    projection = projections_fixtures.TestProjection(get_test_db())
    where = Where("title", "=", "%s") & ~(
        Where("id", "=", "%s") | Where("title", "=", "%s"))
    results = projection.select(where=where, params=("hello", 1, "goodbye"))
    assert len(results) == 0
    results = projection.select(where=where, params=("goodbye", 1, "hello"))
    assert len(results) == 1


def test_projection_query_where_nand(prepare_test):
    projection = projections_fixtures.TestProjection(get_test_db())
    where = Where("title", "=", "%s") & ~(
        Where("id", "=", "%s") & Where("title", "=", "%s"))
    results = projection.select(where=where, params=("goodbye", 2, "goodbye"))
    assert len(results) == 0
    results = projection.select(where=where, params=("hello", 2, "goodbye"))
    assert len(results) == 1


def test_projection_query_where_raise(prepare_test):
    where = Where("title", "=", "%s")
    pytest.raises(TypeError, where.__and__, "a string")
    pytest.raises(TypeError, where.__or__, "a string")


def test_projection_results_empty(prepare_test):
    projection = projections_fixtures.TestProjection(get_test_db())
    query = "select title from dummy where introduction = %s"
    params = ["nothing"]
    assert len(projection.select(query=query, params=params)) == 0