""""""
import sqlalchemy as sa
import sqlalchemy.exc
from pytest import mark, raises

from abilian.testing.util import path_from_url
from abilian.web import url_for

from .models import BaseVocabulary, Vocabulary
from .service import vocabularies

# DefaultVoc = Vocabulary('defaultstates', group='', label='States')

# XXX: we need to use globals for now.
# Classes are global state in Python.
Voc = Vocabulary("voc", label="Voc", group="")
PriorityVoc = Vocabulary("priorities", label="Priorities", group="")
StateVoc = Vocabulary("defaultstates", group="", label="States")
DocCatVoc = Vocabulary("categories", group="documents", label="Categories")


def test_vocabulary_factory(session):
    assert Voc.__name__ == "VocabularyVoc"
    assert Voc.__tablename__ == "vocabulary_voc"
    assert Voc.Meta.name == "voc"
    assert Voc.Meta.label == "Voc"
    assert Voc.Meta.group == ""
    assert issubclass(Voc, BaseVocabulary)


def test_vocabularies(session):
    # test registered vocabularies
    assert vocabularies.vocabularies == {Voc, PriorityVoc, StateVoc, DocCatVoc}

    grouped = vocabularies.grouped_vocabularies
    assert set(grouped[""]) == {Voc, StateVoc, PriorityVoc}
    assert grouped["documents"] == [DocCatVoc]

    assert vocabularies.get_vocabulary("priorities") is PriorityVoc
    assert vocabularies.get_vocabulary("priorities", "nogroup") is None
    assert vocabularies.get_vocabulary("categories", "documents") is DocCatVoc


def test_items(db, session):
    db.create_all()

    IMMEDIATE = PriorityVoc(label="Immediate", position=0)
    NORMAL = PriorityVoc(label="Normal", position=3, default=True)
    URGENT = PriorityVoc(label="Urgent", position=1)
    HIGH = PriorityVoc(label="High", position=2)
    items = (IMMEDIATE, NORMAL, URGENT, HIGH)
    for item in items:
        session.add(item)
    session.flush()

    # test position=4 set automatically; Label stripped
    low_item = PriorityVoc(label=" Low  ")
    session.add(low_item)
    session.flush()
    assert low_item.position == 4
    assert low_item.label == "Low"

    # test strip label on update
    IMMEDIATE.label = "  Immediate  "
    session.flush()
    assert IMMEDIATE.label == "Immediate"

    # test default ordering
    default_ordering = ["Immediate", "Urgent", "High", "Normal", "Low"]
    query = PriorityVoc.query
    assert [i.label for i in query.active().all()] == default_ordering

    # no default ordering when using .values(): explicit ordering required
    query = PriorityVoc.query.active().order_by(PriorityVoc.position.asc())
    assert [i.label for i in query.values(PriorityVoc.label)] == default_ordering

    # test db-side constraint for non-empty labels
    with raises(sa.exc.IntegrityError):
        with session.begin_nested():
            v = PriorityVoc(label="   ", position=6)
            session.add(v)
            session.flush()

    # test unique labels constraint
    with raises(sa.exc.IntegrityError):
        with session.begin_nested():
            v = PriorityVoc(label="Immediate")
            session.add(v)
            session.flush()

    # test unique position constraint
    with raises(sa.exc.IntegrityError):
        with session.begin_nested():
            v = PriorityVoc(label="New one", position=1)
            session.add(v)
            session.flush()

    # test by_position without results
    item = PriorityVoc.query.by_position(42)
    assert item is None

    # test by_position() and active()
    item = PriorityVoc.query.by_position(URGENT.position)
    assert item is URGENT

    item.active = False
    expected = ["Immediate", "High", "Normal", "Low"]
    assert [i.label for i in PriorityVoc.query.active().all()] == expected
    assert PriorityVoc.query.active().by_position(URGENT.position) is None

    # test by_label()
    item = PriorityVoc.query.by_label(NORMAL.label)
    assert item is NORMAL


@mark.skip
def test_admin_panel_reorder(app, db, session, client, test_request_context):
    db.create_all()
    items = [
        Voc(label="First", position=0),
        Voc(label="Second", position=2),
        Voc(label="Third", position=3),
    ]

    for i in items:
        session.add(i)
    session.commit()

    first, second, third = items
    url = url_for("admin.vocabularies")
    base_data = {"Model": Voc.Meta.name}
    data = {"down": first.id}
    data.update(base_data)
    r = client.post(url, data=data)
    assert r.status_code == 302
    assert path_from_url(r.location) == "/admin/vocabularies"
    assert Voc.query.order_by(Voc.position).all() == [second, first, third]

    data = {"up": first.id, "return_to": "group"}
    data.update(base_data)
    r = client.post(url, data=data)
    assert r.status_code == 302
    assert path_from_url(r.location) == "/admin/vocabularies/_/"
    assert Voc.query.order_by(Voc.position).all() == [first, second, third]

    data = {"up": first.id, "return_to": "model"}
    data.update(base_data)
    r = client.post(url, data=data)
    assert r.status_code == 302
    assert path_from_url(r.location) == "/admin/vocabularies/_/defaultstates/"
    assert Voc.query.order_by(Voc.position).all() == [first, second, third]

    data = {"down": third.id}
    data.update(base_data)
    r = client.post(url, data=data)
    assert r.status_code == 302
    assert path_from_url(r.location) == "/admin/vocabularies"
    assert Voc.query.order_by(Voc.position).all() == [first, second, third]

    data = {"up": third.id}
    data.update(base_data)
    r = client.post(url, data=data)
    assert r.status_code == 302
    assert path_from_url(r.location) == "/admin/vocabularies"
    assert Voc.query.order_by(Voc.position).all() == [first, third, second]
