import typing as t
from contextlib import asynccontextmanager

from piccolo.engine import engine_finder
from piccolo.utils.pydantic import create_pydantic_model
from piccolo_admin.endpoints import create_admin
from starlette.staticfiles import StaticFiles
from xpresso import App, FromJson, FromPath, HTTPException, Operation, Path
from xpresso.routing.mount import Mount

from home.endpoints import home
from home.piccolo_app import APP_CONFIG
from home.tables import Task

TaskModelIn: t.Any = create_pydantic_model(table=Task, model_name="TaskModelIn")
TaskModelOut: t.Any = create_pydantic_model(
    table=Task, include_default_columns=True, model_name="TaskModelOut"
)


async def tasks() -> t.List[TaskModelOut]:
    return await Task.select().order_by(Task.id)


async def create_task(task_model: FromJson[TaskModelIn]) -> TaskModelOut:
    task = Task(**task_model.dict())
    await task.save()
    return task.to_dict()


async def update_task(
    task_id: FromPath[int], task_model: FromJson[TaskModelIn]
) -> TaskModelOut:
    task = await Task.objects().get(Task.id == task_id)
    if not task:
        raise HTTPException(status_code=404)

    for key, value in task_model.dict().items():
        setattr(task, key, value)

    await task.save()

    return task.to_dict()


async def delete_task(task_id: FromPath[int]):
    task = await Task.objects().get(Task.id == task_id)
    if not task:
        raise HTTPException(status_code=404)

    await task.remove()

    return {}


@asynccontextmanager
async def lifespan():
    await open_database_connection_pool()
    try:
        yield
    finally:
        await close_database_connection_pool()


app = App(
    routes=[
        Path(
            "/",
            get=Operation(
                home,
                include_in_schema=False,
            ),
        ),
        Mount(
            "/admin/",
            create_admin(
                tables=APP_CONFIG.table_classes,
                # Required when running under HTTPS:
                # allowed_hosts=['my_site.com']
            ),
        ),
        Path(
            "/tasks/",
            get=tasks,
            post=create_task,
            tags=["Task"],
        ),
        Path(
            "/tasks/{task_id}/",
            put=update_task,
            delete=delete_task,
            tags=["Task"],
        ),
        Mount("/static/", StaticFiles(directory="static")),
    ],
    lifespan=lifespan,
)


async def open_database_connection_pool():
    try:
        engine = engine_finder()
        await engine.start_connection_pool()
    except Exception:
        print("Unable to connect to the database")


async def close_database_connection_pool():
    try:
        engine = engine_finder()
        await engine.close_connection_pool()
    except Exception:
        print("Unable to connect to the database")
