# (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

from app_home.log import log, DEBUG

from app_user.aaa_api import start_api

from app_data.models import DataTask
from app_data.task_manager import abort_task

import app_data.api.helper as helper


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)
