# retoor <retoor@molodetz.nl>

import logging

from fastapi import APIRouter, Request, WebSocket
from starlette.responses import Response

from devplacepy.services.containers import activity, api, forward, store
from devplacepy.services.containers.workspace import naming, tunnels

logger = logging.getLogger(__name__)
router = APIRouter()

METHODS = forward.METHODS


def resolve(host: str):
    if not naming.is_tunnel_host(host):
        return None, None, None, None
    row = tunnels.by_hostname(host)
    if not row or row.get("status") not in tunnels.SERVING_STATUSES:
        return None, None, None, None
    instance = store.get_instance(row.get("instance_uid", ""))
    if not instance or instance.get("deleted_at"):
        return None, None, None, None
    if instance.get("suspended_at"):
        return row, instance, None, None
    if instance.get("status") != store.ST_RUNNING:
        return row, instance, None, None
    host, port = api.tunnel_target(instance, int(row.get("container_port") or 0))
    return row, instance, host, port


async def handle_http(request: Request, path: str) -> Response:
    host = request.headers.get("host", "")
    row, instance, gateway, port = resolve(host)
    if row is None:
        return Response("no tunnel is published at this address", status_code=404)
    if instance is not None and instance.get("suspended_at"):
        return Response("this workspace is suspended", status_code=403)
    if not gateway or not port:
        return Response("the tunnel has no reachable port", status_code=502)
    return await forward.proxy_http(
        request,
        gateway,
        port,
        path,
        on_complete=lambda sent: record_traffic(instance["uid"], row["uid"], sent),
    )


def record_traffic(instance_uid: str, tunnel_uid: str, sent: int) -> None:
    activity.touch(instance_uid, egress_bytes=sent)
    tunnels.record_hit(tunnel_uid, sent)


async def handle_ws(websocket: WebSocket, path: str) -> None:
    host = websocket.headers.get("host", "")
    row, instance, gateway, port = resolve(host)
    if row is None or instance is None or not gateway or not port:
        await websocket.close(code=1011)
        return
    activity.touch(instance["uid"])
    await forward.proxy_ws(websocket, gateway, port, path)
