from fastapi.testclient import TestClient
from sqlmodel import Session, select

from app.db.session import engine, init_db, seed_database
from app.main import app
from app.models import TaskCompletion, TaskInstance, TaskStatus


def _bonus_task_id(payload):
    for child in payload["children"]:
        for task in child["tasks"]["open"]:
            if task["kind"] == "bonus":
                return child["child"]["id"], task["id"]
    raise AssertionError("no bonus task found")


def _wuschi_task(payload, *, status: str = "open"):
    for child in payload["children"]:
        for task in child["tasks"][status]:
            if task["title"] == "Wuschi füttern":
                return child["child"]["id"], task["id"], task
    raise AssertionError(f"no Wuschi task found in {status}")


def _all_wuschi_tasks(payload, *, status: str = "open"):
    matches = []
    for child in payload["children"]:
        for task in child["tasks"][status]:
            if task["title"] == "Wuschi füttern":
                matches.append((child["child"]["id"], task["id"], task))
    return matches


def _required_task_id(payload):
    for child in payload["children"]:
        for task in child["tasks"]["open"]:
            if task["kind"] == "required":
                return child["child"]["id"], task["id"]
    raise AssertionError("no required task found")


def test_bonus_complete_is_idempotent_and_ledger_updates():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-22").json()
        child_id, task_id = _bonus_task_id(dashboard)
        before = client.get(f"/api/coins/{child_id}/ledger").json()["summary"]

        first = client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "tap-1"})
        second = client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "tap-1"})
        ledger = client.get(f"/api/coins/{child_id}/ledger")

    assert first.status_code == 200
    assert first.json()["coin_transaction"]["amount"] == 2
    assert second.status_code == 200
    assert second.json()["idempotent"] is True
    assert ledger.json()["summary"] == {
        "posted": before["posted"] + 2,
        "reserved": before["reserved"],
        "available": before["available"] + 2,
    }
    assert len([tx for tx in ledger.json()["transactions"] if tx["comment"].startswith("Bonusaufgabe erledigt:")]) == 1


def test_bonus_undo_creates_counter_transaction():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-18").json()
        child_id, task_id = _bonus_task_id(dashboard)
        before = client.get(f"/api/coins/{child_id}/ledger").json()["summary"]
        client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "tap-2"})
        undo = client.post(f"/api/tasks/{task_id}/undo", json={"idempotency_key": "undo-2"})
        ledger = client.get(f"/api/coins/{child_id}/ledger")

    assert undo.status_code == 200
    assert undo.json()["coin_transaction"]["amount"] == -2
    assert undo.json()["task"]["status"] == "open"
    assert ledger.json()["summary"] == before
    assert any(tx["amount"] == -2 for tx in ledger.json()["transactions"])


def test_required_complete_does_not_book_plus_coins():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-29").json()
        child_id, task_id = _required_task_id(dashboard)
        before = client.get(f"/api/coins/{child_id}/ledger").json()["summary"]
        complete = client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "required-1"})
        ledger = client.get(f"/api/coins/{child_id}/ledger")

    assert complete.status_code == 200
    transaction = complete.json()["coin_transaction"]
    assert transaction is None or transaction["transaction_type"] == "penalty_reversal"
    assert complete.json()["task"]["status"] == "done"
    expected_delta = transaction["amount"] if transaction else 0
    assert ledger.json()["summary"]["posted"] == before["posted"] + expected_delta
    assert ledger.json()["summary"]["available"] == before["available"] + expected_delta


def test_bonus_task_complete_persists_done_after_dashboard_reload():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-22").json()
        _, task_id, _ = _wuschi_task(dashboard, status="open")
        complete = client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "wuschi-done"})
        reloaded = client.get("/api/dashboard?date=2026-06-22").json()

    assert complete.status_code == 200
    _, reloaded_id, task = _wuschi_task(reloaded, status="done")
    assert reloaded_id == task_id
    assert task["status"] == "done"


def test_shared_bonus_task_disappears_for_both_children_when_completed_once():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-22").json()
        open_wuschi = _all_wuschi_tasks(dashboard, status="open")
        assert len(open_wuschi) == 2
        completing_child_id, task_id, _ = open_wuschi[0]
        before = client.get(f"/api/coins/{completing_child_id}/ledger").json()["summary"]

        complete = client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "shared-wuschi-done"})
        reloaded = client.get("/api/dashboard?date=2026-06-22").json()
        completing_ledger = client.get(f"/api/coins/{completing_child_id}/ledger").json()

    assert complete.status_code == 200
    assert _all_wuschi_tasks(reloaded, status="open") == []
    done_wuschi = _all_wuschi_tasks(reloaded, status="done")
    assert len(done_wuschi) == 2
    assert {task["status"] for _, _, task in done_wuschi} == {"done"}
    assert completing_ledger["summary"] == {
        "posted": before["posted"] + 2,
        "reserved": before["reserved"],
        "available": before["available"] + 2,
    }
    bonus_transactions = [tx for tx in completing_ledger["transactions"] if tx["comment"].startswith("Bonusaufgabe erledigt: Wuschi")]
    assert len(bonus_transactions) == 1


def test_shared_bonus_task_undo_reopens_for_both_children_and_reverses_once():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-23").json()
        first_child_id, task_id, _ = _all_wuschi_tasks(dashboard, status="open")[0]
        before = client.get(f"/api/coins/{first_child_id}/ledger").json()["summary"]
        client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "shared-wuschi-done-undo"})
        done_dashboard = client.get("/api/dashboard?date=2026-06-23").json()
        sibling_done_task_id = [item for item in _all_wuschi_tasks(done_dashboard, status="done") if item[1] != task_id][0][1]

        undo = client.post(f"/api/tasks/{sibling_done_task_id}/undo", json={"idempotency_key": "shared-wuschi-open"})
        reloaded = client.get("/api/dashboard?date=2026-06-23").json()
        ledger = client.get(f"/api/coins/{first_child_id}/ledger").json()

    assert undo.status_code == 200
    assert len(_all_wuschi_tasks(reloaded, status="open")) == 2
    assert _all_wuschi_tasks(reloaded, status="done") == []
    assert ledger["summary"] == before
    bonus_transactions = [tx for tx in ledger["transactions"] if "Wuschi füttern" in tx["comment"]]
    assert sorted(tx["amount"] for tx in bonus_transactions) == [-2, 2]


def test_bonus_task_undo_persists_open_after_dashboard_reload():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-23").json()
        _, task_id, _ = _wuschi_task(dashboard, status="open")
        client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "wuschi-done-undo"})
        undo = client.post(f"/api/tasks/{task_id}/undo", json={"idempotency_key": "wuschi-open"})
        reloaded = client.get("/api/dashboard?date=2026-06-23").json()

    assert undo.status_code == 200
    _, reloaded_id, task = _wuschi_task(reloaded, status="open")
    assert reloaded_id == task_id
    assert task["status"] == "open"
    assert all(task["id"] != task_id for child in reloaded["children"] for task in child["tasks"]["done"])


def test_bonus_task_undo_survives_seed_database():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-24").json()
        _, task_id, _ = _wuschi_task(dashboard, status="open")
        client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "wuschi-seed-done"})
        client.post(f"/api/tasks/{task_id}/undo", json={"idempotency_key": "wuschi-seed-open"})

    with Session(engine) as session:
        seed_database()
    with TestClient(app) as client:
        reloaded = client.get("/api/dashboard?date=2026-06-24").json()

    _, reloaded_id, task = _wuschi_task(reloaded, status="open")
    assert reloaded_id == task_id
    assert task["status"] == "open"


def test_bonus_task_undo_survives_backend_restart_simulation():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-25").json()
        _, task_id, _ = _wuschi_task(dashboard, status="open")
        client.post(f"/api/tasks/{task_id}/complete", json={"idempotency_key": "wuschi-session-done"})
        client.post(f"/api/tasks/{task_id}/undo", json={"idempotency_key": "wuschi-session-open"})

    with Session(engine) as session:
        instance = session.get(TaskInstance, task_id)
        assert instance.status == TaskStatus.open
        assert instance.completed_at is None

    with TestClient(app) as client:
        reloaded = client.get("/api/dashboard?date=2026-06-25").json()
    _, reloaded_id, task = _wuschi_task(reloaded, status="open")
    assert reloaded_id == task_id
    assert task["status"] == "open"


def test_taskcompletion_does_not_override_taskinstance_status():
    init_db()
    with TestClient(app) as client:
        dashboard = client.get("/api/dashboard?date=2026-06-26").json()
        child_id, task_id, _ = _wuschi_task(dashboard, status="open")

    with Session(engine) as session:
        instance = session.get(TaskInstance, task_id)
        session.add(TaskCompletion(task_instance_id=task_id, child_id=child_id, idempotency_key="historical-completion"))
        instance.status = TaskStatus.open
        instance.completed_at = None
        session.add(instance)
        session.commit()

    with TestClient(app) as client:
        reloaded = client.get("/api/dashboard?date=2026-06-26").json()
    _, reloaded_id, task = _wuschi_task(reloaded, status="open")
    assert reloaded_id == task_id
    assert task["status"] == "open"


def test_api_responses_are_no_store():
    init_db()
    with TestClient(app) as client:
        response = client.get("/api/dashboard?date=2026-06-27")
    assert response.headers["cache-control"] == "no-store, max-age=0"
