from datetime import time

import pytest
from sqlalchemy import delete
from sqlmodel import Session, select

from app.db.session import engine, hash_pin, init_db, seed_database
from app.models import AdminCredential, AppSetting, Benefit, Child, ChildSettings, ScheduleBlock, ScheduleBlockType, ScheduleDay, TaskKind, TaskTemplate


def test_seed_database_is_idempotent():
    init_db()
    with Session(engine) as session:
        seed_database()
        seed_database()
        children = session.exec(select(Child)).all()
        templates = session.exec(select(TaskTemplate)).all()
        benefits = session.exec(select(Benefit)).all()

    assert [child.name for child in children] == ["Emilia", "Valerie"]
    assert len(templates) == 8
    assert len(benefits) == 3


def test_core_seed_entities_exist():
    init_db()
    with Session(engine) as session:
        emilia = session.exec(select(Child).where(Child.name == "Emilia")).one()
        settings = session.exec(select(ChildSettings).where(ChildSettings.child_id == emilia.id)).one()
        schedule_days = session.exec(select(ScheduleDay).where(ScheduleDay.child_id == emilia.id)).all()
        admin = session.exec(select(AdminCredential).where(AdminCredential.username == "admin")).one()

    assert settings.commute_minutes == 15
    assert len(schedule_days) == 5
    assert admin.pin_hash.startswith("pbkdf2_sha256$")
    assert "9999" not in admin.pin_hash


def test_pin_hash_uses_random_salt():
    first = hash_pin("9999")
    second = hash_pin("9999")
    assert first != second
    assert "9999" not in first


def test_seed_database_preserves_admin_task_and_schedule_block_after_reopen():
    init_db()
    with Session(engine) as session:
        emilia = session.exec(select(Child).where(Child.name == "Emilia")).one()
        day = session.exec(select(ScheduleDay).where(ScheduleDay.child_id == emilia.id, ScheduleDay.weekday == 5)).one()
        task = TaskTemplate(
            child_id=emilia.id,
            slug="admin-extra-task",
            title="Admin Zusatzaufgabe",
            kind=TaskKind.bonus,
            coin_value=7,
            penalty_value=0,
            applies_weekdays="0,1,2,3,4,5,6",
            sort_order=999,
        )
        block = ScheduleBlock(
            schedule_day_id=day.id,
            child_id=emilia.id,
            weekday=5,
            title="Admin Spezialtermin",
            starts_at=time(16, 10),
            ends_at=time(16, 55),
            block_type=ScheduleBlockType.other,
            subject="Admin Spezialtermin",
            counts_as_school=False,
            affects_timeline=True,
            active=True,
        )
        session.add(task)
        session.add(block)
        session.commit()

    with Session(engine) as session:
        assert session.exec(select(TaskTemplate).where(TaskTemplate.slug == "admin-extra-task", TaskTemplate.active == True)).first() is not None  # noqa: E712
        assert session.exec(select(ScheduleBlock).where(ScheduleBlock.title == "Admin Spezialtermin", ScheduleBlock.active == True)).first() is not None  # noqa: E712

    seed_database()

    with Session(engine) as session:
        assert session.exec(select(TaskTemplate).where(TaskTemplate.slug == "admin-extra-task", TaskTemplate.active == True)).first() is not None  # noqa: E712
        assert session.exec(select(ScheduleBlock).where(ScheduleBlock.title == "Admin Spezialtermin", ScheduleBlock.active == True)).first() is not None  # noqa: E712


def test_seed_database_does_not_overwrite_existing_reference_schedule_block():
    init_db()
    with Session(engine) as session:
        emilia = session.exec(select(Child).where(Child.name == "Emilia")).one()
        emilia_id = emilia.id
        block = session.exec(select(ScheduleBlock).where(
            ScheduleBlock.child_id == emilia_id,
            ScheduleBlock.weekday == 2,
            ScheduleBlock.title == "Gitarre ZI 205",
            ScheduleBlock.active == True,  # noqa: E712
        )).one()
        block.location = "Admin-Ort"
        block.notes = "Admin geändert"
        session.add(block)
        session.commit()

    seed_database()

    with Session(engine) as session:
        block = session.exec(select(ScheduleBlock).where(
            ScheduleBlock.child_id == emilia_id,
            ScheduleBlock.weekday == 2,
            ScheduleBlock.title == "Gitarre ZI 205",
            ScheduleBlock.active == True,  # noqa: E712
        )).one()
        assert block.location == "Admin-Ort"
        assert block.notes == "Admin geändert"


def test_initialized_database_with_missing_core_rows_fails_fast():
    init_db()
    with Session(engine) as session:
        marker = session.get(AppSetting, "production_db_initialized")
        assert marker is not None
        session.exec(delete(ScheduleBlock))
        session.commit()

    with pytest.raises(RuntimeError, match="Persistenzfehler"):
        init_db()
