from fastapi.testclient import TestClient

from app.db.session import init_db
from app.main import app
from tests.conftest import admin_headers


def test_admin_crud_routes_require_login():
    init_db()
    with TestClient(app) as client:
        response = client.get("/api/admin/task-templates")
    assert response.status_code == 401


def test_children_task_templates_benefits_and_settings_persist_after_reload():
    init_db()
    with TestClient(app) as client:
        headers = admin_headers(client)
        child = client.get("/api/admin/children", headers=headers).json()["children"][0]
        child_id = child["id"]

        renamed = client.patch(
            f"/api/admin/children/{child_id}",
            headers=headers,
            json={"name": "Emilia Test", "avatar": "🌟"},
        )
        template = client.post(
            "/api/admin/task-templates",
            headers=headers,
            json={
                "child_id": child_id,
                "slug": "test-lesezeit",
                "title": "10 Minuten lesen",
                "kind": "bonus",
                "coin_value": 4,
                "penalty_value": 0,
                "applies_weekdays": "0,1,2,3,4,5,6",
            },
        )
        benefit = client.post(
            "/api/admin/benefits",
            headers=headers,
            json={"slug": "test-ausflug", "title": "Mini-Ausflug", "price": 14, "description": "Kleines Ziel aussuchen."},
        )
        setting = client.patch(
            "/api/admin/settings/coin_name",
            headers=headers,
            json={"value": "Sterne", "description": "Testname"},
        )

        init_db()
        children_after = client.get("/api/admin/children", headers=headers).json()["children"]
        templates_after = client.get("/api/admin/task-templates", headers=headers).json()["task_templates"]
        benefits_after = client.get("/api/admin/benefits", headers=headers).json()["benefits"]
        settings_after = client.get("/api/admin/settings", headers=headers).json()["settings"]

    assert renamed.status_code == 200
    assert renamed.json()["child"]["name"] == "Emilia Test"
    assert template.status_code == 201
    assert template.json()["task_template"]["coin_value"] == 4
    assert benefit.status_code == 201
    assert benefit.json()["benefit"]["price"] == 14
    assert setting.status_code == 200
    assert any(item["name"] == "Emilia Test" for item in children_after)
    assert any(item["slug"] == "test-lesezeit" for item in templates_after)
    assert any(item["slug"] == "test-ausflug" for item in benefits_after)
    assert any(item["key"] == "coin_name" and item["value"] == "Sterne" for item in settings_after)


def test_admin_benefit_and_task_template_inline_updates_persist_after_reload():
    init_db()
    with TestClient(app) as client:
        headers = admin_headers(client)
        child_id = client.get("/api/admin/children", headers=headers).json()["children"][0]["id"]

        benefit = client.post(
            "/api/admin/benefits",
            headers=headers,
            json={"slug": "inline-test-benefit", "title": "Inline Test", "price": 5, "description": "Alt"},
        ).json()["benefit"]
        patched_benefit = client.patch(
            f"/api/admin/benefits/{benefit['id']}",
            headers=headers,
            json={"title": "Inline Test Neu", "price": 9, "description": "Neu", "child_id": child_id},
        )
        deleted_benefit = client.delete(f"/api/admin/benefits/{benefit['id']}", headers=headers)

        template = client.post(
            "/api/admin/task-templates",
            headers=headers,
            json={
                "child_id": child_id,
                "slug": "inline-test-task",
                "title": "Inline Aufgabe",
                "kind": "bonus",
                "coin_value": 2,
                "penalty_value": 0,
            },
        ).json()["task_template"]
        patched_template = client.patch(
            f"/api/admin/task-templates/{template['id']}",
            headers=headers,
            json={"kind": "required", "coin_value": 0, "penalty_value": -3, "title": "Inline Pflicht"},
        )
        deactivated_template = client.patch(
            f"/api/admin/task-templates/{template['id']}",
            headers=headers,
            json={"active": False},
        )
        reactivated_template = client.patch(
            f"/api/admin/task-templates/{template['id']}",
            headers=headers,
            json={"active": True},
        )
        deleted_template = client.delete(f"/api/admin/task-templates/{template['id']}", headers=headers)

        init_db()
        benefits_after = client.get("/api/admin/benefits", headers=headers).json()["benefits"]
        templates_after = client.get("/api/admin/task-templates", headers=headers).json()["task_templates"]

    assert patched_benefit.status_code == 200
    assert patched_benefit.json()["benefit"]["price"] == 9
    assert deleted_benefit.status_code == 200
    stored_benefit = next(item for item in benefits_after if item["id"] == benefit["id"])
    assert stored_benefit["title"] == "Inline Test Neu"
    assert stored_benefit["active"] is False
    assert stored_benefit["child_id"] == child_id

    assert patched_template.status_code == 200
    assert patched_template.json()["task_template"]["penalty_value"] == -3
    assert deactivated_template.status_code == 200
    assert deactivated_template.json()["task_template"]["active"] is False
    assert reactivated_template.status_code == 200
    assert reactivated_template.json()["task_template"]["active"] is True
    assert deleted_template.status_code == 200
    assert deleted_template.json()["deleted"] is True
    assert all(item["id"] != template["id"] for item in templates_after)


def test_manual_coin_deduction_requires_comment_and_updates_ledger():
    init_db()
    with TestClient(app) as client:
        headers = admin_headers(client)
        child_id = client.get("/api/admin/children", headers=headers).json()["children"][0]["id"]
        missing_comment = client.post(
            "/api/admin/coins/manual",
            headers=headers,
            json={"child_id": child_id, "amount": -2, "comment": ""},
        )
        plus = client.post(
            "/api/admin/coins/manual",
            headers=headers,
            json={"child_id": child_id, "amount": 5, "comment": "Startbonus", "idempotency_key": "manual-plus"},
        )
        minus = client.post(
            "/api/admin/coins/manual",
            headers=headers,
            json={"child_id": child_id, "amount": -2, "comment": "Korrektur", "idempotency_key": "manual-minus"},
        )
        ledger = client.get(f"/api/coins/{child_id}/ledger").json()["summary"]
        admin_ledger = client.get(f"/api/admin/coins/{child_id}/ledger", headers=headers).json()

    assert missing_comment.status_code == 422
    assert plus.status_code == 200
    assert minus.status_code == 200
    assert ledger == {"posted": 3, "reserved": 0, "available": 3}
    assert admin_ledger["summary"] == ledger
    assert admin_ledger["child"]["id"] == child_id


def test_admin_coin_void_creates_idempotent_counter_booking():
    init_db()
    with TestClient(app) as client:
        headers = admin_headers(client)
        child_id = client.get("/api/admin/children", headers=headers).json()["children"][0]["id"]
        plus = client.post(
            "/api/admin/coins/manual",
            headers=headers,
            json={"child_id": child_id, "amount": 2, "comment": "Testbuchung", "idempotency_key": "void-base"},
        ).json()["transaction"]
        voided = client.post(f"/api/admin/coins/{plus['id']}/void", headers=headers, json={"comment": "Teststorno"})
        duplicate = client.post(f"/api/admin/coins/{plus['id']}/void", headers=headers, json={"comment": "Teststorno"})
        ledger = client.get(f"/api/admin/coins/{child_id}/ledger", headers=headers).json()

    assert voided.status_code == 200
    assert duplicate.status_code == 200
    assert voided.json()["transaction"]["amount"] == -2
    assert voided.json()["transaction"]["id"] == duplicate.json()["transaction"]["id"]
    assert ledger["summary"] == {"posted": 0, "reserved": 0, "available": 0}
    assert any(tx["reference_transaction_id"] == plus["id"] for tx in ledger["transactions"])


def test_special_day_and_schedule_crud():
    init_db()
    with TestClient(app) as client:
        headers = admin_headers(client)
        child_id = client.get("/api/admin/children", headers=headers).json()["children"][0]["id"]
        special = client.post(
            "/api/admin/special-days",
            headers=headers,
            json={"child_id": child_id, "day": "2026-07-01", "day_type": "special", "title": "Ausflug"},
        )
        special_id = special.json()["special_day"]["id"]
        patch_special = client.patch(
            f"/api/admin/special-days/{special_id}",
            headers=headers,
            json={"title": "Ausflug Zoo"},
        )
        schedule_day = client.put(
            "/api/admin/schedule-days",
            headers=headers,
            json={"child_id": child_id, "weekday": 2, "school_start": "08:30", "school_end": "15:00", "is_school_day": True},
        )
        block = client.post(
            "/api/admin/schedule-blocks",
            headers=headers,
            json={
                "schedule_day_id": schedule_day.json()["schedule_day"]["id"],
                "starts_at": "09:00",
                "ends_at": "09:45",
                "subject": "Musik",
                "requires_sports_gear": False,
            },
        )
        block_id = block.json()["schedule_block"]["id"]
        patched_block = client.patch(f"/api/admin/schedule-blocks/{block_id}", headers=headers, json={"room": "Aula"})

    assert special.status_code == 201
    assert patch_special.json()["special_day"]["title"] == "Ausflug Zoo"
    assert schedule_day.status_code == 200
    assert block.status_code == 201
    assert patched_block.json()["schedule_block"]["room"] == "Aula"
