# retoor <retoor@molodetz.nl>

from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse

from devplacepy.schemas import DbMutationOut, DbRowOut, DbRowsOut
from devplacepy.services.dbapi import crud
from devplacepy.services.dbapi.crud import DbApiError

from ._shared import (
    assert_table,
    error,
    parse_filters,
    read_body,
    require_dbapi_caller,
    row_payload,
)

router = APIRouter()


def _target_uid(row: dict, value: str) -> str:
    return (row or {}).get("uid") or str(value)


@router.get("/{table}")
async def dbapi_list(
    request: Request,
    table: str,
    limit: int = 25,
    before: str = None,
    search: str = "",
    include_deleted: bool = False,
):
    require_dbapi_caller(request)
    assert_table(table)
    filters, comparisons = parse_filters(request)
    rows, next_cursor = crud.list_rows(
        table,
        filters=filters,
        comparisons=comparisons,
        search=search,
        before=before,
        limit=limit,
        include_deleted=include_deleted,
    )
    return JSONResponse(
        DbRowsOut(
            table=table, rows=rows, count=len(rows), next_cursor=next_cursor
        ).model_dump(mode="json")
    )


@router.get("/{table}/{key}/{value}")
async def dbapi_get(request: Request, table: str, key: str, value: str):
    require_dbapi_caller(request)
    assert_table(table)
    try:
        row = crud.get_row(table, key, value)
    except DbApiError as exc:
        return error(400, str(exc))
    if row is None:
        return error(404, "Row not found")
    return JSONResponse(DbRowOut(table=table, row=row).model_dump(mode="json"))


@router.post("/{table}")
async def dbapi_insert(request: Request, table: str):
    caller = require_dbapi_caller(request)
    assert_table(table)
    body = await read_body(request)
    try:
        row = crud.insert_row(table, row_payload(body), caller.uid)
    except DbApiError as exc:
        return error(400, str(exc))
    _audit(request, "database.row.insert", table, _target_uid(row, ""), caller)
    return JSONResponse(
        DbMutationOut(table=table, ok=True, mode="insert", row=row).model_dump(mode="json")
    )


@router.patch("/{table}/{key}/{value}")
async def dbapi_update(request: Request, table: str, key: str, value: str):
    caller = require_dbapi_caller(request)
    assert_table(table)
    body = await read_body(request)
    try:
        row = crud.update_row(table, key, value, row_payload(body), caller.uid)
    except DbApiError as exc:
        return error(400, str(exc))
    if row is None:
        return error(404, "Row not found")
    _audit(request, "database.row.update", table, _target_uid(row, value), caller)
    return JSONResponse(
        DbMutationOut(table=table, ok=True, mode="update", row=row).model_dump(mode="json")
    )


@router.delete("/{table}/{key}/{value}")
async def dbapi_delete(
    request: Request, table: str, key: str, value: str, hard: bool = False
):
    caller = require_dbapi_caller(request)
    assert_table(table)
    try:
        outcome = crud.delete_row(table, key, value, caller.uid, hard=hard)
    except DbApiError as exc:
        return error(400, str(exc))
    if outcome is None:
        return error(404, "Row not found")
    _audit(
        request,
        "database.row.delete",
        table,
        _target_uid(outcome.get("row", {}), value),
        caller,
        metadata={"mode": outcome["mode"]},
    )
    return JSONResponse(
        DbMutationOut(
            table=table, ok=True, mode=outcome["mode"], row=outcome.get("row")
        ).model_dump(mode="json")
    )


@router.post("/{table}/{key}/{value}/restore")
async def dbapi_restore(request: Request, table: str, key: str, value: str):
    caller = require_dbapi_caller(request)
    assert_table(table)
    try:
        row = crud.restore_row(table, key, value, caller.uid)
    except DbApiError as exc:
        return error(400, str(exc))
    if row is None:
        return error(404, "Row not found")
    _audit(request, "database.row.restore", table, _target_uid(row, value), caller)
    return JSONResponse(
        DbMutationOut(table=table, ok=True, mode="restore", row=row).model_dump(mode="json")
    )


def _audit(request, event_key, table, target_uid, caller, metadata=None):
    from devplacepy.services.audit import record as audit

    audit.record(
        request,
        event_key,
        target_type=table,
        target_uid=target_uid,
        summary=f"{caller.username or caller.kind} {event_key} on {table}/{target_uid}",
        metadata={"table": table, "caller": caller.kind, **(metadata or {})},
    )
