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, pending_confirmations_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 events.types.child_chore_confirmation import ChildChoreConfirmation
from models.task import Task

task_api = Blueprint('task_api', __name__)

# Task endpoints
@task_api.route('/task/add', methods=['PUT'])
def add_task():
    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')
    task_type = data.get('type')
    # Support legacy is_good field
    if task_type is None and 'is_good' in data:
        task_type = 'chore' if data['is_good'] else 'penalty'
    image = data.get('image_id', '')
    if not name or points is None or task_type is None:
        return jsonify({'error': 'Name, points, and type are required'}), 400
    if task_type not in ['chore', 'kindness', 'penalty']:
        return jsonify({'error': 'type must be chore, kindness, or penalty'}), 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'Task {name} added.'}), 201

@task_api.route('/task/<id>', methods=['GET'])
def get_task(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)))
    if not result:
        return jsonify({'error': 'Task not found'}), 404
    return jsonify(result[0]), 200

@task_api.route('/task/list', methods=['GET'])
def list_tasks():
    user_id = get_validated_user_id()
    if not user_id:
        return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401
    ids_param = request.args.get('ids')
    TaskQuery = Query()
    tasks = task_db.search((TaskQuery.user_id == user_id) | (TaskQuery.user_id == None))
    if ids_param is not None:
        if ids_param.strip() == '':
            tasks = []
        else:
            ids = set(ids_param.split(','))
            tasks = [task for task in tasks if task.get('id') in ids]

    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  # Skip default if user version exists
        filtered_tasks.append(t)
    
    # Sort order:
    # 1) chore/kindness first, then penalties
    # 2) within each group: user-created items first (by name), then default items (by name)
    good_tasks = [t for t in filtered_tasks if Task.from_dict(t).type != 'penalty']
    not_good_tasks = [t for t in filtered_tasks if Task.from_dict(t).type == 'penalty']

    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(good_tasks) + sort_user_then_default(not_good_tasks)
    
    return jsonify({'tasks': sorted_tasks}), 200

@task_api.route('/task/<id>', methods=['DELETE'])
def delete_task(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)
    if not task:
        return jsonify({'error': 'Task not found'}), 404
    if task.get('user_id') is None:
        import logging
        logging.warning(f"Forbidden delete attempt on system task: id={id}, by user_id={user_id}")
        return jsonify({'error': 'System tasks cannot be deleted.'}), 403
    removed = task_db.remove((TaskQuery.id == id) & (TaskQuery.user_id == user_id))
    if removed:
        # Cascade delete overrides for this task
        deleted_count = delete_overrides_for_entity(id)
        if deleted_count > 0:
            import logging
            logging.info(f"Cascade deleted {deleted_count} overrides for task {id}")
        
        # remove the task id from any child's task list
        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'Task {id} deleted.'}), 200
    return jsonify({'error': 'Task not found'}), 404

@task_api.route('/task/<id>/edit', methods=['PUT'])
def edit_task(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)))
    if not existing:
        return jsonify({'error': 'Task not found'}), 404

    task = Task.from_dict(existing)
    is_dirty = False

    data = request.get_json(force=True) or {}
    updates = {}

    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):
            return jsonify({'error': 'Points must be an integer'}), 400
        if points <= 0:
            return jsonify({'error': 'Points must be a positive integer'}), 400
        task.points = points
        is_dirty = True

    if 'is_good' in data:
        is_good = data.get('is_good')
        if not isinstance(is_good, bool):
            return jsonify({'error': 'is_good must be a boolean'}), 400
        # Convert to type
        task.type = 'chore' if is_good else 'penalty'
        is_dirty = True
    
    if 'type' in data:
        task_type = data.get('type')
        if task_type not in ['chore', 'kindness', 'penalty']:
            return jsonify({'error': 'type must be chore, kindness, or penalty'}), 400
        task.type = task_type
        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

    # Reset pending chore confirmations when task is modified (points, type, etc.)
    # Completed (approved) chores stay in completed state
    if 'points' in data or 'type' in data:
        PendingQuery = Query()
        pending_chores = pending_confirmations_db.search(
            (PendingQuery.entity_id == id) & (PendingQuery.entity_type == 'chore') &
            (PendingQuery.status == 'pending')
        )
        for pc in pending_chores:
            pending_confirmations_db.remove(
                (PendingQuery.child_id == pc['child_id']) & (PendingQuery.entity_id == id) &
                (PendingQuery.entity_type == 'chore') & (PendingQuery.status == 'pending')
            )
            send_event_for_current_user(Event(EventType.CHILD_CHORE_CONFIRMATION.value,
                ChildChoreConfirmation(pc['child_id'], id, ChildChoreConfirmation.OPERATION_RESET)))

    if task.user_id is None: # public task
        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