import pytest
from sqlalchemy.exc import InvalidRequestError

from lolpedia_api import func, select, tables, text
from lolpedia_api.cargo_compiler import CargoQueryCompiler

P = tables.Player
PR = tables.PlayerRedirect
T = tables.Tournament
TP = tables.TournamentPlayer
SG = tables.ScoreboardGame
MSG = tables.MatchScheduleGame
MS = tables.MatchSchedule
SP = tables.ScoreboardPlayer
SG = tables.ScoreboardGame


@pytest.fixture
def compiler():
    return CargoQueryCompiler()


def test_query_simple(compiler: CargoQueryCompiler):
    # btw this query wouldn't compile but this isn't really an issue, we're just testing the compiler
    statement = select(P.Player).where(text("FIELD > 1"))
    compiled = compiler.compile(statement)

    assert compiled is not None
    assert compiled["fields"] == f"{P.__tablename__}.Player"
    assert compiled["join_on"] is None
    assert compiled["having"] is None
    assert compiled["group_by"] is None
    assert compiled["where"] == "FIELD > 1"
    assert compiled["tables"] == P.__tablename__
    assert compiled["order_by"] is None
    assert compiled["offset"] is None
    assert compiled["limit"] is None


def test_limit_offset(compiler: CargoQueryCompiler):
    statement = select(P.Player).limit(10).offset(10)
    compiled = compiler.compile(statement)

    assert compiled is not None
    assert compiled["limit"] == 10
    assert compiled["offset"] == 10


def test_joins(compiler: CargoQueryCompiler):
    statement = (
        select(P.Player, P.Name, P.Country, P.Birthdate, P.Residency, P.Role)
        .select_from(T)
        .join(TP, T.OverviewPage == TP.OverviewPage)
        .join(PR, TP.Player == PR.AllName)
        .join(P, PR.OverviewPage == P.OverviewPage)
    )
    compiled = compiler.compile(statement)

    assert compiled is not None
    assert (
        compiled["tables"]
        == f"{T.__tablename__},{TP.__tablename__},{PR.__tablename__},{P.__tablename__}"
    )
    assert compiled["join_on"] == (
        f"{T.__tablename__}.OverviewPage={TP.__tablename__}.OverviewPage,"
        f"{TP.__tablename__}.Player={PR.__tablename__}.AllName,"
        f"{PR.__tablename__}.OverviewPage={P.__tablename__}.OverviewPage"
    )


def test_joins_no_select_from(compiler: CargoQueryCompiler):
    statement = (
        select(P.Player, P.Name, P.Country, P.Birthdate, P.Residency, P.Role)
        .join(TP, T.OverviewPage == TP.OverviewPage)
        .join(PR, TP.Player == PR.AllName)
        .join(P, PR.OverviewPage == P.OverviewPage)
    )
    compiled = compiler.compile(statement)

    assert compiled is not None
    assert (
        compiled["tables"]
        == f"{T.__tablename__},{TP.__tablename__},{PR.__tablename__},{P.__tablename__}"
    )
    assert compiled["join_on"] == (
        f"{T.__tablename__}.OverviewPage={TP.__tablename__}.OverviewPage,"
        f"{TP.__tablename__}.Player={PR.__tablename__}.AllName,"
        f"{PR.__tablename__}.OverviewPage={P.__tablename__}.OverviewPage"
    )


def test_many_where_expect_and(compiler: CargoQueryCompiler):
    statement = select(P).where(P.Age >= 18).where(P.Country == "France")
    compiled = compiler.compile(statement)

    assert compiled is not None
    assert (
        compiled["where"] is not None
        and " AND " in compiled["where"]
        and len(compiled["where"].split(" AND ")) == 2
    )


def test_func_with_label(compiler: CargoQueryCompiler):
    statement = select(P.Country, func.max(P.Age).label("MaxAge")).group_by(P.Country)
    compiled = compiler.compile(statement)

    assert compiled is not None
    assert (
        compiled["fields"] is not None
        and f"{P.__tablename__}.Country,max({P.__tablename__}.Age)=MaxAge"
        == compiled["fields"]
    )


def test_group_by_and_having(compiler: CargoQueryCompiler):
    statement = (
        select(P.Country, func.count().label("Count"))
        .group_by(P.Country)
        .having(text("Count > 10"))
    )
    compiled = compiler.compile(statement)

    assert compiled is not None
    assert (
        compiled["group_by"] is not None
        and compiled["group_by"] == f"{P.__tablename__}.Country"
    )
    assert compiled["having"] is not None and compiled["having"] == "Count > 10"


def test_order_by(compiler: CargoQueryCompiler):
    statement = select(P.Player).order_by(P.Age.desc())
    compiled = compiler.compile(statement)
    assert (
        compiled["order_by"] is not None and compiled["order_by"] == "Players.Age DESC"
    )


def test_many_order_by(compiler: CargoQueryCompiler):
    statement = select(P.Player).order_by(P.Age.desc()).order_by(P.FavChamps.desc())
    compiled = compiler.compile(statement)
    assert (
        compiled["order_by"] is not None and len(compiled["order_by"].split(",")) == 2
    )


def test_empty_select(compiler: CargoQueryCompiler):
    statement = select().order_by(P.Age.desc())

    with pytest.raises(InvalidRequestError):
        compiler.compile(statement)


def test_unsupported_expression_type(compiler: CargoQueryCompiler):
    """Test that complex unsupported expressions raise TypeError"""
    with pytest.raises(TypeError, match="Couldn't process"):
        from sqlalchemy import literal

        statement = select(literal(1) + literal(2))
        compiler.compile(statement)
