from flask import Flask
from tinydb import Query
from werkzeug.security import generate_password_hash

from tests.conftest import TEST_SECRET_KEY, TEST_REFRESH_TOKEN_EXPIRY_DAYS
from api.auth_api import auth_api
from api.routine_api import routine_api
from api.routine_item_api import routine_item_api
from api.child_routine_api import child_routine_api
from api.routine_schedule_api import routine_schedule_api
from db.db import (
    users_db,
    child_db,
    routine_db,
    routine_items_db,
    routine_schedules_db,
    routine_extensions_db,
    pending_confirmations_db,
)


TEST_USER_ID = 'routine-user-1'
TEST_EMAIL = 'routine-user@example.com'
TEST_PASSWORD = 'testpass'


def add_test_user():
    users_db.remove(Query().email == TEST_EMAIL)
    users_db.insert({
        'id': TEST_USER_ID,
        'first_name': 'Routine',
        'last_name': 'Tester',
        'email': TEST_EMAIL,
        'password': generate_password_hash(TEST_PASSWORD),
        'verified': True,
        'image_id': 'boy01',
    })


def login_and_set_cookie(client):
    resp = client.post('/auth/login', json={'email': TEST_EMAIL, 'password': TEST_PASSWORD})
    assert resp.status_code == 200


def seed_child(child_id: str):
    child_db.remove(Query().id == child_id)
    child_db.insert({
        'id': child_id,
        'name': 'Routine Kid',
        'age': 9,
        'tasks': [],
        'routines': [],
        'rewards': [],
        'points': 0,
        'image_id': 'boy01',
        'user_id': TEST_USER_ID,
    })


def _first_routine_id():
    routines = routine_db.all()
    assert routines
    return routines[0]['id']


def _first_confirmation_id():
    confirmations = pending_confirmations_db.all()
    assert confirmations
    return confirmations[0]['id']


def _make_client():
    app = Flask(__name__)
    app.register_blueprint(auth_api, url_prefix='/auth')
    app.register_blueprint(routine_api)
    app.register_blueprint(routine_item_api)
    app.register_blueprint(child_routine_api)
    app.register_blueprint(routine_schedule_api)
    app.config['TESTING'] = True
    app.config['SECRET_KEY'] = TEST_SECRET_KEY
    app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = TEST_REFRESH_TOKEN_EXPIRY_DAYS
    return app


def setup_function(_):
    routine_db.truncate()
    routine_items_db.truncate()
    routine_schedules_db.truncate()
    routine_extensions_db.truncate()
    pending_confirmations_db.truncate()
    child_db.truncate()


def test_routine_crud_flow():
    app = _make_client()
    with app.test_client() as client:
        add_test_user()
        login_and_set_cookie(client)

        add_resp = client.put('/routine/add', json={'name': 'Morning Routine', 'points': 8, 'image_id': 'sun'})
        assert add_resp.status_code == 201

        rid = _first_routine_id()

        list_resp = client.get('/routine/list')
        assert list_resp.status_code == 200
        routines = list_resp.get_json()['routines']
        assert len(routines) == 1
        assert routines[0]['name'] == 'Morning Routine'

        edit_resp = client.put(f'/routine/{rid}/edit', json={'points': 10})
        assert edit_resp.status_code == 200
        assert edit_resp.get_json()['points'] == 10

        delete_resp = client.delete(f'/routine/{rid}')
        assert delete_resp.status_code == 200
        assert routine_db.all() == []


def test_child_routine_assignment_confirmation_and_approval():
    app = _make_client()
    with app.test_client() as client:
        add_test_user()
        login_and_set_cookie(client)

        seed_child('routine-child-1')
        client.put('/routine/add', json={'name': 'Evening Routine', 'points': 6, 'image_id': 'moon'})
        rid = _first_routine_id()

        add_item_resp = client.put(f'/routine/{rid}/item/add', json={'name': 'Brush Teeth', 'order': 0})
        assert add_item_resp.status_code == 201

        assign_resp = client.post('/child/routine-child-1/assign-routine', json={'routine_id': rid})
        assert assign_resp.status_code == 200

        list_resp = client.get('/child/routine-child-1/list-routines')
        assert list_resp.status_code == 200
        routines = list_resp.get_json()['routines']
        assert len(routines) == 1
        assert routines[0]['id'] == rid
        assert routines[0]['items'][0]['name'] == 'Brush Teeth'

        confirm_resp = client.post('/child/routine-child-1/confirm-routine', json={'routine_id': rid})
        assert confirm_resp.status_code == 200

        confirmation_id = _first_confirmation_id()
        approve_resp = client.post(f'/child/routine-child-1/approve-routine/{confirmation_id}')
        assert approve_resp.status_code == 200

        child = child_db.get(Query().id == 'routine-child-1')
        assert child['points'] == 6


def test_routine_schedule_and_extend_flow():
    app = _make_client()
    with app.test_client() as client:
        add_test_user()
        login_and_set_cookie(client)

        seed_child('routine-child-2')
        client.put('/routine/add', json={'name': 'School Routine', 'points': 5, 'image_id': 'book'})
        rid = _first_routine_id()

        client.post('/child/routine-child-2/assign-routine', json={'routine_id': rid})

        set_resp = client.put(
            f'/child/routine-child-2/routine/{rid}/schedule',
            json={
                'mode': 'days',
                'day_configs': [{'day': 1, 'hour': 8, 'minute': 0}],
                'default_hour': 8,
                'default_minute': 0,
                'default_has_deadline': True,
            },
        )
        assert set_resp.status_code == 200

        get_resp = client.get(f'/child/routine-child-2/routine/{rid}/schedule')
        assert get_resp.status_code == 200
        assert get_resp.get_json()['mode'] == 'days'

        extend_resp = client.post(
            f'/child/routine-child-2/routine/{rid}/extend',
            json={'date': '2026-05-04'},
        )
        assert extend_resp.status_code == 200
        assert extend_resp.get_json()['routine_id'] == rid
