from __future__ import annotations

from typing import Any

from fastapi import FastAPI, HTTPException, Query

from .config import settings
from .models import AccountPath, ContractViewRequest, ContractViewResponse, ErrorResponse, Network, NetworkInfo
from .near_client import NearRPCClient, NearRPCError


def create_app() -> FastAPI:
    app = FastAPI(
        title="NEAR FastAPI",
        version="0.1.0",
        description="FastAPI service for common NEAR Protocol operations",
    )

    client = NearRPCClient()

    @app.get("/health")
    async def health() -> dict[str, str]:
        return {"status": "ok", "service": settings.app_name, "env": settings.app_env}

    @app.get("/v1/networks", response_model=list[NetworkInfo])
    async def networks() -> list[NetworkInfo]:
        return [
            NetworkInfo(network=Network.mainnet, rpc_url=settings.mainnet_rpc_url),
            NetworkInfo(network=Network.testnet, rpc_url=settings.testnet_rpc_url),
        ]

    @app.get("/v1/status")
    async def status(network: Network = Query(default=Network.mainnet)) -> dict[str, Any]:
        try:
            return await client.network_status(network)
        except NearRPCError as err:
            raise HTTPException(status_code=502, detail={"error": str(err), "rpc": err.payload}) from err

    @app.get("/v1/accounts/{account_id}")
    async def account(account_id: str, network: Network = Query(default=Network.mainnet)) -> dict[str, Any]:
        try:
            account_id = AccountPath(account_id=account_id).account_id
            return await client.view_account(account_id, network)
        except ValueError as err:
            raise HTTPException(status_code=422, detail={"error": str(err)}) from err
        except NearRPCError as err:
            raise HTTPException(status_code=502, detail={"error": str(err), "rpc": err.payload}) from err

    @app.get("/v1/accounts/{account_id}/keys")
    async def account_keys(account_id: str, network: Network = Query(default=Network.mainnet)) -> dict[str, Any]:
        try:
            account_id = AccountPath(account_id=account_id).account_id
            return await client.access_keys(account_id, network)
        except ValueError as err:
            raise HTTPException(status_code=422, detail={"error": str(err)}) from err
        except NearRPCError as err:
            raise HTTPException(status_code=502, detail={"error": str(err), "rpc": err.payload}) from err

    @app.get("/v1/contracts/{account_id}/code-hash")
    async def code_hash(account_id: str, network: Network = Query(default=Network.mainnet)) -> dict[str, Any]:
        try:
            account_id = AccountPath(account_id=account_id).account_id
            return await client.contract_code_hash(account_id, network)
        except ValueError as err:
            raise HTTPException(status_code=422, detail={"error": str(err)}) from err
        except NearRPCError as err:
            raise HTTPException(status_code=502, detail={"error": str(err), "rpc": err.payload}) from err

    @app.post(
        "/v1/contracts/{account_id}/view",
        response_model=ContractViewResponse,
        responses={502: {"model": ErrorResponse}},
    )
    async def contract_view(account_id: str, request: ContractViewRequest) -> ContractViewResponse:
        try:
            account_id = AccountPath(account_id=account_id).account_id
            result = await client.view_function(
                account_id=account_id,
                method_name=request.method,
                args=request.args,
                network=request.network,
                finality=request.finality,
            )
            return ContractViewResponse(**result)
        except ValueError as err:
            raise HTTPException(status_code=422, detail={"error": str(err)}) from err
        except NearRPCError as err:
            raise HTTPException(status_code=502, detail={"error": str(err), "rpc": err.payload}) from err

    return app
