import pytest
from sqlalchemy import create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from idlemaxx.models import (
    IdlemaxBase,
    Character,
    CharacterSkill,
    Skill,
    ActivityCategory,
    now_utc,
)


@pytest.fixture(scope="function")
def session() -> Session:
    engine = create_engine("sqlite:///:memory:")
    IdlemaxBase.metadata.create_all(engine)
    return Session(engine)


def test_character_skill_unique(session: Session) -> None:
    with pytest.raises(IntegrityError):
        character = Character("Foo")
        skill_1 = CharacterSkill(character.id, Skill.MINING)
        skill_2 = CharacterSkill(character.id, Skill.MINING)
        session.add_all([skill_1, skill_2])
        session.commit()


def test_character_name_unique(session: Session) -> None:
    with pytest.raises(IntegrityError):
        character_1 = Character(name="Foo")
        character_2 = Character(name="Foo")
        session.add_all([character_1, character_2])
        session.commit()


def test_default_experience() -> None:
    character = Character(name="Foo")
    character_skill = CharacterSkill(character.id, Skill.MINING)
    assert character_skill.experience == 0


def test_created_at(session: Session) -> None:
    before = now_utc()
    character = Character("Foo")
    after = now_utc()
    assert character.created_at is not None
    assert before <= character.created_at <= after


def test_updated_at(session: Session) -> None:
    before = now_utc()
    character = Character("Foo")
    after = now_utc()
    assert character.updated_at is not None
    assert before <= character.updated_at <= after

    original_updated_at = character.updated_at

    session.add(character)
    session.commit()

    character.name = "Bar"
    session.commit()
    session.refresh(character)

    assert character.updated_at > original_updated_at


def test_every_skill_has_a_category() -> None:
    skill_names = {s.name for s in Skill}
    category_names = {c.name for c in ActivityCategory}
    assert skill_names <= category_names, f"Missing categories for skills: {skill_names - category_names}"


def test_character_skill_cascade(session: Session) -> None:
    character = Character("Foo")
    session.add(character)

    character_skill = CharacterSkill(character.id, Skill.MINING)
    character.skills.append(character_skill)

    session.commit()

    assert session.get(CharacterSkill, character_skill.id) is not None
