"""Todo CRUD endpoints."""

from __future__ import annotations

from datetime import datetime
from typing import Any

from aiohttp import web

from dobby.models import Priority, Todo


async def create_todo(request: web.Request) -> web.Response:
    store = request.app["store"]
    try:
        body = await request.json()
    except Exception as exc:
        raise web.HTTPBadRequest(reason=f"Invalid JSON: {exc}") from exc
    if not body.get("description"):
        raise web.HTTPBadRequest(reason="'description' is required")

    kwargs: dict[str, Any] = {"description": body["description"]}
    if body.get("project"):
        kwargs["project"] = body["project"]
    if body.get("priority"):
        kwargs["priority"] = Priority(body["priority"])
    if body.get("due_date"):
        kwargs["due_date"] = datetime.fromisoformat(body["due_date"])
    if body.get("tags"):
        kwargs["tags"] = body["tags"]
    if body.get("source_task_id"):
        kwargs["source_task_id"] = body["source_task_id"]
    if body.get("reminder_cron"):
        kwargs["reminder_cron"] = body["reminder_cron"]

    todo = Todo(**kwargs)
    await store.create_todo(todo)
    return web.json_response(todo.to_dict(), status=201)


async def list_todos(request: web.Request) -> web.Response:
    store = request.app["store"]
    project = request.rel_url.query.get("project")
    status = request.rel_url.query.get("status")
    due_date_before = request.rel_url.query.get("due_date_before")
    todos = await store.list_todos(
        project=project, status=status, due_date_before=due_date_before,
    )
    return web.json_response([t.to_dict() for t in todos])


async def get_todo(request: web.Request) -> web.Response:
    store = request.app["store"]
    todo_id = request.match_info["id"]
    try:
        todo = await store.get_todo(todo_id)
    except KeyError as exc:
        raise web.HTTPNotFound(reason=f"Todo not found: {todo_id}") from exc
    return web.json_response(todo.to_dict())


async def patch_todo(request: web.Request) -> web.Response:
    store = request.app["store"]
    todo_id = request.match_info["id"]
    try:
        body = await request.json()
    except Exception as exc:
        raise web.HTTPBadRequest(reason=f"Invalid JSON: {exc}") from exc

    fields: dict[str, Any] = {}
    if "status" in body:
        if body["status"] not in ("open", "done", "cancelled"):
            raise web.HTTPBadRequest(reason=f"Invalid status: {body['status']}")
        fields["status"] = body["status"]
    if "priority" in body:
        fields["priority"] = body["priority"]
    if "due_date" in body:
        fields["due_date"] = body["due_date"]
    if "description" in body:
        fields["description"] = body["description"]
    if "tags" in body:
        fields["tags"] = body["tags"]
    if "reminder_cron" in body:
        fields["reminder_cron"] = body["reminder_cron"]

    try:
        updated = await store.update_todo(todo_id, **fields)
    except KeyError as exc:
        raise web.HTTPNotFound(reason=f"Todo not found: {todo_id}") from exc
    return web.json_response(updated.to_dict())


async def delete_todo(request: web.Request) -> web.Response:
    store = request.app["store"]
    todo_id = request.match_info["id"]
    deleted = await store.delete_todo(todo_id)
    if not deleted:
        raise web.HTTPNotFound(reason=f"Todo not found: {todo_id}")
    return web.json_response({"deleted": todo_id})
