from datetime import date, datetime
from zoneinfo import ZoneInfo

from sqlmodel import Session, select

from app.db.session import engine, init_db
from app.models import Child
from app.services.dashboard import (
    child_timeline,
    get_afternoon_leave_home_time,
    get_first_afternoon_school_block,
    get_next_external_activity,
)

TZ = ZoneInfo("Europe/Zurich")


def child_by_name(session: Session, name: str) -> Child:
    child = session.exec(select(Child).where(Child.name == name)).one()
    return child


def timeline_for(name: str, target: date, hour: int, minute: int):
    init_db()
    with Session(engine) as session:
        child = child_by_name(session, name)
        now = datetime(target.year, target.month, target.day, hour, minute, tzinfo=TZ)
        return child_timeline(session, child, target, "Europe/Zurich", now=now)


def test_morning_countdown_uses_teeth_before_departure_not_afternoon_target():
    emilia = timeline_for("Emilia", date(2026, 6, 23), 7, 37)
    valerie = timeline_for("Valerie", date(2026, 6, 23), 7, 37)

    assert emilia["dayPhase"] == "morning"
    assert emilia["primaryMessage"] == "Zähneputzen in 18 Min."
    assert emilia["remaining_minutes"] == 18
    assert emilia["morningTeethStart"] == "07:55"
    assert emilia["nextDepartureTime"] == "12:40"
    assert emilia["minutesUntilNextDeparture"] == 303

    assert valerie["dayPhase"] == "morning"
    assert valerie["primaryMessage"] == "Zähneputzen in 18 Min."
    assert valerie["remaining_minutes"] == 18
    assert valerie["morningTeethStart"] == "07:55"
    assert valerie["nextDepartureTime"] == "13:15"
    assert valerie["minutesUntilNextDeparture"] == 338


def test_morning_after_teeth_start_counts_down_to_departure():
    timeline = timeline_for("Emilia", date(2026, 6, 23), 7, 58)

    assert timeline["dayPhase"] == "morning"
    assert timeline["primaryMessage"] == "Loslaufen in 7 Min."
    assert timeline["remaining_minutes"] == 7
    assert timeline["minutesUntilTeeth"] == -3


def test_lunch_break_with_afternoon_school_at_1210_has_countdown():
    timeline = timeline_for("Emilia", date(2026, 6, 15), 12, 10)

    assert timeline["dayPhase"] == "lunch_break"
    assert timeline["statusLevel"] == "calm"
    assert timeline["minutesUntilLeaveHome"] == 65
    assert timeline["afternoonLeaveHomeTime"] == "13:15"
    assert timeline["afternoonSchoolStart"] == "13:30"
    assert timeline["primaryMessage"] == "Loslaufen in 65 Min."
    lunch_step = next(step for step in timeline["timelineSteps"] if step["key"] == "lunch")
    prepare_step = next(step for step in timeline["timelineSteps"] if step["key"] == "teeth_lunch")
    assert lunch_step["kind"] == "range"
    assert lunch_step["label"] == "Mittagessen"
    assert lunch_step["shortLabel"] == "Mittag"
    assert lunch_step["icon"] == "🍽️"
    assert lunch_step["time"] == "11:50"
    assert lunch_step["endTime"] == "13:00"
    assert prepare_step["time"] == "13:00"
    assert prepare_step["endTime"] == "13:15"
    assert prepare_step["icon"] == "🪥🚽 👟"


def test_lunch_break_at_1310_is_urgent():
    timeline = timeline_for("Emilia", date(2026, 6, 15), 13, 10)

    assert timeline["dayPhase"] == "lunch_break"
    assert timeline["statusLevel"] == "urgent"
    assert timeline["minutesUntilLeaveHome"] == 5
    assert timeline["primaryMessage"] in {"Jetzt bereit machen", "Loslaufen in 5 Min."}


def test_after_leave_before_school_is_late_now_leave():
    timeline = timeline_for("Emilia", date(2026, 6, 15), 13, 16)

    assert timeline["dayPhase"] == "lunch_break"
    assert timeline["statusLevel"] == "late"
    assert timeline["primaryMessage"] == "Jetzt loslaufen"


def test_after_afternoon_school_start_is_school_running():
    timeline = timeline_for("Emilia", date(2026, 6, 15), 13, 31)

    assert timeline["dayPhase"] == "afternoon_school"
    assert timeline["primaryMessage"] == "Noch 94 Min."
    assert timeline["secondaryMessage"] == "Schule läuft bis 15:05"
    assert timeline["minutesUntilSchoolEnd"] == 94
    assert timeline["schoolEndTime"] == "15:05"


def test_school_running_today_timeline_keeps_lunch_teeth_and_no_end_marker():
    timeline = timeline_for("Valerie", date(2026, 6, 16), 10, 0)

    assert timeline["dayPhase"] == "school_running"
    steps = timeline["timelineSteps"]
    by_key = {step["key"]: step for step in steps}
    assert "school_end" not in by_key
    assert all(step["shortLabel"] != "Ende" for step in steps)
    assert by_key["morning_school"]["endTime"] == by_key["lunch"]["time"]
    assert by_key["lunch"]["label"] == "Mittagessen"
    assert by_key["lunch"]["shortLabel"] == "Mittag"
    assert by_key["lunch"]["icon"] == "🍽️"
    assert "lunch_break" not in by_key
    assert by_key["lunch"]["endTime"] == by_key["teeth_lunch"]["time"]
    assert by_key["teeth_lunch"]["icon"] == "🪥🚽 👟"
    assert by_key["teeth_lunch"]["endTime"] == by_key["way_to_school"]["time"]
    assert by_key["way_to_school"]["endTime"] == by_key["afternoon_school"]["time"]
    assert by_key["morning_school"]["detail"] == "Heute Morgen: Schule"
    assert by_key["afternoon_school"]["detail"] == "Heute Nachmittag: Turnen"
    assert all(step["key"] != "homework" for step in steps)

    emilia_timeline = timeline_for("Emilia", date(2026, 6, 16), 10, 0)
    emilia_by_key = {step["key"]: step for step in emilia_timeline["timelineSteps"]}
    assert emilia_by_key["morning_school"]["detail"] == "Heute Morgen: Turnen"


def test_child_without_afternoon_school_is_afternoon_free_and_external_hint():
    timeline = timeline_for("Emilia", date(2026, 6, 16), 12, 10)

    assert timeline["dayPhase"] == "lunch_break"
    assert timeline["statusLevel"] in {"calm", "soon"}
    assert timeline["primaryMessage"] == "Loslaufen in 30 Min."
    assert timeline["nextDepartureTarget"] == "Gitarre"
    assert timeline["nextDepartureTime"] == "12:40"
    assert timeline["hasAfternoonSchool"] is False
    assert timeline["nextExternalActivity"]["title"] == "Gitarre ZI 205"
    lunch = next(step for step in timeline["timelineSteps"] if step["key"] == "lunch")
    guitar = next(step for step in timeline["timelineSteps"] if step["label"] == "Gitarre ZI 205")
    assert lunch["label"] == "Mittagessen"
    assert lunch["icon"] == "🍽️"
    assert guitar["label"] == "Gitarre ZI 205"
    assert guitar["time"] == "12:50"
    assert guitar["endTime"] == "13:15"
    assert guitar["icon"] == "🎸"
    assert guitar["detail"] == "Heute Nachmittag: Gitarre ZI 205"
    assert all(step["key"] != "homework" for step in timeline["timelineSteps"])


def test_emilia_after_guitar_is_free_with_dinner_far_target():
    timeline = timeline_for("Emilia", date(2026, 6, 16), 13, 16)

    assert timeline["dayPhase"] == "afternoon_free"
    assert timeline["primaryMessage"] == "Nachmittag frei"
    steps = timeline["timelineSteps"]
    free = next(step for step in steps if step["label"] == "Frei" and step["time"] == "13:15")
    dinner = next(step for step in steps if step["key"] == "dinner")
    assert free["icon"] == "🌿"
    assert free["endTime"] == "18:00"
    assert not any(
        step.get("label") == "Frei" and step.get("endTime") == next_step.get("time") and next_step.get("label") == "Frei"
        for step, next_step in zip(steps, steps[1:])
    )
    assert dinner["label"] == "Nachtessen"
    assert dinner["time"] == "18:00"
    assert dinner["icon"] == "🍽️"


def test_valerie_tuesday_lunch_has_no_pause_gap_and_school_icons():
    timeline = timeline_for("Valerie", date(2026, 6, 16), 12, 10)
    by_key = {step["key"]: step for step in timeline["timelineSteps"]}

    assert by_key["lunch"]["time"] == "11:50"
    assert by_key["lunch"]["endTime"] == "13:00"
    assert "lunch_break" not in by_key
    assert by_key["teeth_lunch"]["time"] == "13:00"
    assert by_key["teeth_lunch"]["endTime"] == "13:15"
    assert by_key["teeth_lunch"]["icon"] == "🪥🚽 👟"
    assert by_key["way_to_school"]["time"] == "13:15"
    assert by_key["way_to_school"]["icon"] == "🚶"
    assert by_key["afternoon_school"]["time"] == "13:30"
    assert by_key["afternoon_school"]["icon"] == "🏫"


def test_external_activity_does_not_count_as_school_and_commute_is_15_minutes():
    init_db()
    with Session(engine) as session:
        emilia = child_by_name(session, "Emilia")
        target = date(2026, 6, 16)
        assert get_first_afternoon_school_block(session, emilia.id, target) is None
        assert get_afternoon_leave_home_time(session, emilia, target) is None
        external = get_next_external_activity(session, emilia.id, target)
        assert external.title == "Gitarre ZI 205"

        monday = date(2026, 6, 15)
        assert get_afternoon_leave_home_time(session, emilia, monday).isoformat(timespec="minutes") == "13:15"


def test_different_children_can_have_different_lunch_phases():
    init_db()
    target = date(2026, 6, 16)
    now = datetime(2026, 6, 16, 12, 10, tzinfo=TZ)
    with Session(engine) as session:
        emilia = child_by_name(session, "Emilia")
        valerie = child_by_name(session, "Valerie")
        emilia_timeline = child_timeline(session, emilia, target, "Europe/Zurich", now=now)
        valerie_timeline = child_timeline(session, valerie, target, "Europe/Zurich", now=now)

    assert emilia_timeline["dayPhase"] == "lunch_break"
    assert emilia_timeline["nextDepartureTarget"] == "Gitarre"
    assert valerie_timeline["dayPhase"] == "lunch_break"
    assert valerie_timeline["hasAfternoonSchool"] is True


def test_external_activity_renders_as_time_range_with_real_name():
    timeline = timeline_for("Emilia", date(2026, 6, 15), 15, 30)

    external = next(step for step in timeline["timelineSteps"] if step["key"] == "external")
    assert external["kind"] == "range"
    assert external["shortLabel"] == "Mädchenriege"
    assert external["time"] == "17:30"
    assert external["endTime"] == "19:00"
    assert external["emphasis"] == "external"


def test_future_day_shows_full_day_from_wake_to_sleep_without_los_label():
    init_db()
    target = date(2026, 6, 16)
    now = datetime(2026, 6, 15, 20, 0, tzinfo=TZ)
    with Session(engine) as session:
        valerie = child_by_name(session, "Valerie")
        timeline = child_timeline(session, valerie, target, "Europe/Zurich", now=now)

    labels = [step["shortLabel"] for step in timeline["timelineSteps"]]
    assert timeline["nowLabel"] == "Ganzer Tag"
    assert labels[0] == "Auf"
    assert "Schule" in labels
    assert "Mittag" in labels
    assert "Frei" in labels
    assert labels[-1] == "Schlafen"
    assert "Los" in labels
