from flask import Blueprint, request, jsonify
from tinydb import Query

from api.utils import send_event_for_current_user, get_validated_user_id
from events.types.child_tasks_set import ChildTasksSet
from db.db import task_db, child_db
from db.child_overrides import delete_overrides_for_entity
from events.types.event import Event
from events.types.event_types import EventType
from events.types.task_modified import TaskModified
from models.task import Task

penalty_api = Blueprint('penalty_api', __name__)

TASK_TYPE = 'penalty'


@penalty_api.route('/penalty/add', methods=['PUT'])
def add_penalty():
    user_id = get_validated_user_id()
    if not user_id:
        return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401
    data = request.get_json()
    name = data.get('name')
    points = data.get('points')
    image = data.get('image_id', '')
    if not name or points is None:
        return jsonify({'error': 'Name and points are required'}), 400
    task = Task(name=name, points=points, type=TASK_TYPE, image_id=image, user_id=user_id)
    task_db.insert(task.to_dict())
    send_event_for_current_user(Event(EventType.TASK_MODIFIED.value,
                                      TaskModified(task.id, TaskModified.OPERATION_ADD)))
    return jsonify({'message': f'Penalty {name} added.'}), 201


@penalty_api.route('/penalty/<id>', methods=['GET'])
def get_penalty(id):
    user_id = get_validated_user_id()
    if not user_id:
        return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401
    TaskQuery = Query()
    result = task_db.search(
        (TaskQuery.id == id) &
        ((TaskQuery.user_id == user_id) | (TaskQuery.user_id == None)) &
        (TaskQuery.type == TASK_TYPE)
    )
    if not result:
        return jsonify({'error': 'Penalty not found'}), 404
    return jsonify(result[0]), 200


@penalty_api.route('/penalty/list', methods=['GET'])
def list_penalties():
    user_id = get_validated_user_id()
    if not user_id:
        return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401
    TaskQuery = Query()
    tasks = task_db.search(
        ((TaskQuery.user_id == user_id) | (TaskQuery.user_id == None)) &
        (TaskQuery.type == TASK_TYPE)
    )

    user_tasks = {t['name'].strip().lower(): t for t in tasks if t.get('user_id') == user_id}
    filtered_tasks = []
    for t in tasks:
        if t.get('user_id') is None and t['name'].strip().lower() in user_tasks:
            continue
        filtered_tasks.append(t)

    def sort_user_then_default(tasks_group):
        user_created = sorted(
            [t for t in tasks_group if t.get('user_id') == user_id],
            key=lambda x: x['name'].lower(),
        )
        default_items = sorted(
            [t for t in tasks_group if t.get('user_id') is None],
            key=lambda x: x['name'].lower(),
        )
        return user_created + default_items

    sorted_tasks = sort_user_then_default(filtered_tasks)
    return jsonify({'tasks': sorted_tasks}), 200


@penalty_api.route('/penalty/<id>', methods=['DELETE'])
def delete_penalty(id):
    user_id = get_validated_user_id()
    if not user_id:
        return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401
    TaskQuery = Query()
    task = task_db.get((TaskQuery.id == id) & (TaskQuery.type == TASK_TYPE))
    if not task:
        return jsonify({'error': 'Penalty not found'}), 404
    if task.get('user_id') is None:
        import logging
        logging.warning(f"Forbidden delete attempt on system penalty: id={id}, by user_id={user_id}")
        return jsonify({'error': 'System penalties cannot be deleted.'}), 403
    removed = task_db.remove((TaskQuery.id == id) & (TaskQuery.user_id == user_id))
    if removed:
        deleted_count = delete_overrides_for_entity(id)
        if deleted_count > 0:
            import logging
            logging.info(f"Cascade deleted {deleted_count} overrides for penalty {id}")
        ChildQuery = Query()
        for child in child_db.all():
            child_tasks = child.get('tasks', [])
            if id in child_tasks:
                child_tasks.remove(id)
                child_db.update({'tasks': child_tasks}, ChildQuery.id == child.get('id'))
                send_event_for_current_user(Event(EventType.CHILD_TASKS_SET.value, ChildTasksSet(id, child_tasks)))
        send_event_for_current_user(Event(EventType.TASK_MODIFIED.value, TaskModified(id, TaskModified.OPERATION_DELETE)))
        return jsonify({'message': f'Penalty {id} deleted.'}), 200
    return jsonify({'error': 'Penalty not found'}), 404


@penalty_api.route('/penalty/<id>/edit', methods=['PUT'])
def edit_penalty(id):
    user_id = get_validated_user_id()
    if not user_id:
        return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401
    TaskQuery = Query()
    existing = task_db.get(
        (TaskQuery.id == id) &
        ((TaskQuery.user_id == user_id) | (TaskQuery.user_id == None)) &
        (TaskQuery.type == TASK_TYPE)
    )
    if not existing:
        return jsonify({'error': 'Penalty not found'}), 404

    task = Task.from_dict(existing)
    is_dirty = False
    data = request.get_json(force=True) or {}

    if 'name' in data:
        name = data.get('name', '').strip()
        if not name:
            return jsonify({'error': 'Name cannot be empty'}), 400
        task.name = name
        is_dirty = True

    if 'points' in data:
        points = data.get('points')
        if not isinstance(points, int) or points <= 0:
            return jsonify({'error': 'Points must be a positive integer'}), 400
        task.points = points
        is_dirty = True

    if 'image_id' in data:
        task.image_id = data.get('image_id', '')
        is_dirty = True

    if not is_dirty:
        return jsonify({'error': 'No valid fields to update'}), 400

    if task.user_id is None:
        new_task = Task(name=task.name, points=task.points, type=TASK_TYPE, image_id=task.image_id, user_id=user_id)
        task_db.insert(new_task.to_dict())
        send_event_for_current_user(Event(EventType.TASK_MODIFIED.value,
                                          TaskModified(new_task.id, TaskModified.OPERATION_ADD)))
        return jsonify(new_task.to_dict()), 200

    task_db.update(task.to_dict(), (TaskQuery.id == id) & ((TaskQuery.user_id == user_id) | (TaskQuery.user_id == None)))
    send_event_for_current_user(Event(EventType.TASK_MODIFIED.value,
                                      TaskModified(id, TaskModified.OPERATION_EDIT)))
    return jsonify(task.to_dict()), 200
