# (c) cavaliba.com - data/api - task.py
#
# GET  /api/tasks/                  list tasks (own or all for admin)
# GET  /api/tasks/<handle>/         poll progress
# POST /api/tasks/<handle>/stop/    abort task

from django.views.decorators.csrf import csrf_exempt

import app_data.api.helper as helper
from app_data.api.aaa import start_api
from app_data.models import DataTask
from app_data.task_manager import abort_task
from app_home.log import DEBUG, log


def _task_to_dict(dt):
    return {
        "handle": str(dt.handle),
        "name": dt.name,
        "state": dt.state,
        "owner_type": dt.owner_type,
        "owner_id": dt.owner_id,
        "singleton": dt.singleton,
        "params": dt.params,
        "progress": dt.progress,
        "output": dt.output,
        "attachment": dt.attachment,
        "created_at": dt.created_at.isoformat() if dt.created_at else None,
        "finished_at": dt.finished_at.isoformat() if dt.finished_at else None,
        "duration": dt.duration,
    }


def _is_admin(aaa_api):
    return "p_data_admin" in aaa_api.get("perms", [])


def _owns(aaa_api, dt):
    return aaa_api.get("keyname") == dt.owner_id or aaa_api.get("username") == dt.owner_id


# ------------------------------------------------------------------
# GET /api/tasks/
# ------------------------------------------------------------------


@csrf_exempt
def task_list(request):

    aaa_api = start_api(request, permission="p_data_access")
    if not aaa_api["is_allowed"]:
        return helper.send_denied()

    if request.method != "GET":
        return helper.send_error("method not allowed", 405)

    page = helper.get_page(request)
    size = helper.get_size(request)
    state_filter = request.GET.get("state", "").strip()

    qs = DataTask.objects.all()

    if not _is_admin(aaa_api):
        owner_id = aaa_api.get("keyname") or aaa_api.get("username") or ""
        qs = qs.filter(owner_id=owner_id)

    if state_filter:
        qs = qs.filter(state=state_filter.upper())

    total = qs.count()
    offset = (page - 1) * size
    tasks = qs[offset : offset + size]

    reply = {
        "total": total,
        "page": page,
        "size": size,
        "tasks": [_task_to_dict(dt) for dt in tasks],
    }

    log(
        DEBUG,
        aaa=aaa_api,
        app="api",
        view="task_list",
        action="GET",
        status="OK",
        data=f"total={total}",
    )

    return helper.send_response(request, reply, 200)


# ------------------------------------------------------------------
# GET /api/tasks/<handle>/
# ------------------------------------------------------------------


@csrf_exempt
def task_detail(request, handle):

    aaa_api = start_api(request, permission="p_data_access")
    if not aaa_api["is_allowed"]:
        return helper.send_denied()

    if request.method != "GET":
        return helper.send_error("method not allowed", 405)

    try:
        dt = DataTask.objects.get(handle=handle)
    except (DataTask.DoesNotExist, Exception):
        return helper.send_not_found("task not found")

    if not (_owns(aaa_api, dt) or _is_admin(aaa_api)):
        return helper.send_denied("not your task")

    log(
        DEBUG,
        aaa=aaa_api,
        app="api",
        view="task_detail",
        action="GET",
        status="OK",
        data=f"handle={handle}",
    )

    return helper.send_response(request, _task_to_dict(dt), 200)


# ------------------------------------------------------------------
# POST /api/tasks/<handle>/stop/
# ------------------------------------------------------------------


@csrf_exempt
def task_stop(request, handle):

    aaa_api = start_api(request, permission="p_task_manage")
    if not aaa_api["is_allowed"]:
        return helper.send_denied()

    if request.method != "POST":
        return helper.send_error("method not allowed", 405)

    if aaa_api.get("is_readonly"):
        return helper.send_denied("API key is read-only")

    try:
        dt = DataTask.objects.get(handle=handle)
    except (DataTask.DoesNotExist, Exception):
        return helper.send_not_found("task not found")

    if not (_owns(aaa_api, dt) or _is_admin(aaa_api)):
        return helper.send_denied("not your task")

    ok = abort_task(dt.handle, aaa=aaa_api)

    log(
        DEBUG,
        aaa=aaa_api,
        app="api",
        view="task_stop",
        action="POST",
        status="OK",
        data=f"handle={handle} aborted={ok}",
    )

    reply = {"handle": str(handle), "aborted": ok}
    return helper.send_response(request, reply, 200)
