"""Run with: python -m uvicorn movie_rest:app --host 127.0.0.1 --port 8100."""

import logging
import os
from typing import Annotated

import httpx
from fastapi import FastAPI, HTTPException, Path

app = FastAPI(title="Movie cast service (REST API)")
logger = logging.getLogger(__name__)
base_url = os.environ.get("NEBULA_HTTP_URL", "http://127.0.0.1:19669").rstrip("/")
username = os.environ.get("NEBULA_USER", "root")
password = os.environ["NEBULA_PASSWORD"]


class DatabaseError(Exception):
    pass


def post(client, path, body=None):
    response = client.post(path, json=body)
    response.raise_for_status()
    payload = response.json()
    if not isinstance(payload, dict) or payload.get("code") != "00000":
        raise DatabaseError("Database returned an unsuccessful response")
    return payload.get("data", {})


def query_rows(client, statement):
    data = post(client, "/gql", {"gql": statement})
    if not isinstance(data, dict):
        raise DatabaseError("Missing query data")
    rows = data.get("tables")
    if not isinstance(rows, list) or not all(isinstance(row, dict) for row in rows):
        raise DatabaseError("Invalid query result table")
    return rows


@app.get("/movies/{movie_id}/actors")
def get_actors(movie_id: Annotated[int, Path(ge=1, le=9223372036854775807)]):
    movie_query = (
        "USE /default_schema/movie MATCH (m@Movie) "
        f"WHERE m.id = {movie_id} RETURN m.name AS movie"
    )
    actors_query = (
        "USE /default_schema/movie MATCH (a@Actor)-[:Act]->(m@Movie) "
        f"WHERE m.id = {movie_id} "
        "RETURN a.id AS actor_id, a.name AS actor ORDER BY actor_id"
    )

    try:
        with httpx.Client(base_url=base_url, timeout=10.0) as client:
            auth = post(client, "/auth", {"username": username, "password": password})
            token = auth.get("token") if isinstance(auth, dict) else None
            if not isinstance(token, str) or not token:
                raise DatabaseError("Missing authentication token")
            client.headers["Authorization"] = f"Bearer {token}"
            try:
                movies = query_rows(client, movie_query)
                actors = query_rows(client, actors_query) if movies else []
            finally:
                # Release this request's database session even if a query fails.
                try:
                    post(client, "/signout")
                except Exception as exc:
                    logger.warning("Signout failed (%s)", type(exc).__name__)
    except Exception as exc:
        logger.warning("Database request failed (%s)", type(exc).__name__)
        raise HTTPException(status_code=502, detail="Database request failed") from exc

    if not movies:
        raise HTTPException(status_code=404, detail="Movie not found")

    return {"movie_id": movie_id, "movie": movies[0]["movie"], "actors": actors}
