from datetime import date, timedelta

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

from app.db.session import engine, seed_database
from app.main import app
from app.models import CoinTransaction, TaskInstance, TaskKind, TaskStatus, TaskTemplate
from app.services.day_close import finalize_day
from tests.conftest import admin_headers

client = TestClient(app)


def _child_id() -> int:
    return client.get('/api/admin/children', headers=admin_headers(client)).json()['children'][0]['id']


def _create_template(headers, child_id: int, slug='test-deactivate-task', title='Test Pflicht') -> dict:
    response = client.post('/api/admin/task-templates', headers=headers, json={
        'child_id': child_id,
        'slug': slug,
        'title': title,
        'kind': 'required',
        'coin_value': 0,
        'penalty_value': -1,
        'applies_weekdays': '0,1,2,3,4,5,6',
        'active': True,
    })
    assert response.status_code == 201
    return response.json()['task_template']


def test_deactivate_template_hides_existing_today_instance():
    today = date.today()
    with TestClient(app) as local_client:
        headers = admin_headers(local_client)
        child_id = local_client.get('/api/admin/children', headers=headers).json()['children'][0]['id']
        template = local_client.post('/api/admin/task-templates', headers=headers, json={
            'child_id': child_id, 'slug': 'hide-today-task', 'title': 'Heute ausblenden', 'kind': 'required',
            'coin_value': 0, 'penalty_value': -1, 'applies_weekdays': '0,1,2,3,4,5,6', 'active': True,
        }).json()['task_template']
        first = local_client.get('/api/dashboard', params={'date': today.isoformat()})
        assert any(task['title'] == 'Heute ausblenden' for child in first.json()['children'] for task in child['tasks']['open'])
        deactivated = local_client.patch(f"/api/admin/task-templates/{template['id']}", headers=headers, json={'active': False})
        second = local_client.get('/api/dashboard', params={'date': today.isoformat()})
    assert deactivated.status_code == 200
    assert deactivated.json()['cancelled_instances'] >= 1
    assert not any(task['title'] == 'Heute ausblenden' for child in second.json()['children'] for task in child['tasks']['open'])


def test_delete_template_removes_open_current_and_future_instances_from_admin_list():
    today = date.today()
    tomorrow = today + timedelta(days=1)
    with TestClient(app) as local_client:
        headers = admin_headers(local_client)
        child_id = local_client.get('/api/admin/children', headers=headers).json()['children'][0]['id']
        template = local_client.post('/api/admin/task-templates', headers=headers, json={
            'child_id': child_id, 'slug': 'cancel-open-future', 'title': 'Offen löschen', 'kind': 'required',
            'coin_value': 0, 'penalty_value': -1, 'applies_weekdays': '0,1,2,3,4,5,6', 'active': True,
        }).json()['task_template']
        local_client.get('/api/dashboard', params={'date': today.isoformat()})
        local_client.get('/api/dashboard', params={'date': tomorrow.isoformat()})
        response = local_client.delete(f"/api/admin/task-templates/{template['id']}", headers=headers)
        templates_after = local_client.get('/api/admin/task-templates', headers=headers).json()['task_templates']
    assert response.status_code == 200
    assert response.json()['deleted'] is True
    assert all(item['id'] != template['id'] for item in templates_after)
    with Session(engine) as session:
        rows = session.exec(select(TaskInstance).where(TaskInstance.task_template_id == template['id'])).all()
        assert rows == []


def test_done_instances_not_modified_when_template_deactivated():
    today = date.today()
    with TestClient(app) as local_client:
        headers = admin_headers(local_client)
        child_id = local_client.get('/api/admin/children', headers=headers).json()['children'][0]['id']
        template = local_client.post('/api/admin/task-templates', headers=headers, json={
            'child_id': child_id, 'slug': 'done-stays-done', 'title': 'Erledigt bleibt', 'kind': 'bonus',
            'coin_value': 2, 'penalty_value': 0, 'applies_weekdays': '0,1,2,3,4,5,6', 'active': True,
        }).json()['task_template']
        dashboard = local_client.get('/api/dashboard', params={'date': today.isoformat()}).json()
        task = next(task for child in dashboard['children'] for task in child['tasks']['open'] if task['title'] == 'Erledigt bleibt')
        done = local_client.post(f"/api/tasks/{task['id']}/complete", json={'idempotency_key': 'done-stays-done'})
        assert done.status_code == 200
        local_client.patch(f"/api/admin/task-templates/{template['id']}", headers=headers, json={'active': False})
    with Session(engine) as session:
        instance = session.get(TaskInstance, task['id'])
        assert instance.status == TaskStatus.done
        assert session.exec(select(CoinTransaction).where(CoinTransaction.task_instance_id == task['id'])).first() is not None


def test_finalize_day_ignores_inactive_template_tasks():
    yesterday = date.today() - timedelta(days=1)
    with Session(engine) as session:
        template = TaskTemplate(child_id=1, slug='inactive-required', title='Inactive required', kind=TaskKind.required, coin_value=0, penalty_value=-5, active=False, applies_weekdays='0,1,2,3,4,5,6')
        session.add(template)
        session.flush()
        task = TaskInstance(task_template_id=template.id, child_id=1, task_date=yesterday, title='Inactive required', kind=TaskKind.required, penalty_value=-5, status=TaskStatus.open)
        session.add(task)
        session.commit()
        booked = finalize_day(session, yesterday, date.today())
        session.commit()
        tx = session.exec(select(CoinTransaction).where(CoinTransaction.task_instance_id == task.id)).first()
    assert booked == []
    assert tx is None


def test_seed_does_not_reactivate_deactivated_toothbrushing():
    with Session(engine) as session:
        template = TaskTemplate(child_id=1, slug='zaehneputzen-morgen', title='Zähneputzen morgens', kind=TaskKind.required, active=False, penalty_value=-1)
        session.add(template)
        session.commit()
        template_id = template.id
    seed_database()
    with Session(engine) as session:
        stored = session.get(TaskTemplate, template_id)
    assert stored.active is False
