"""Authenticated internal policy API; existing server command lock fences Start."""
import asyncio
from uuid import uuid4
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse

from app.multi_roi.errors import RoiError


def policy_router(store, token):
    from app.secure_control import authorized, decode, ControlError
    router = APIRouter()

    @router.get('/internal/secure-control/roi-policy')
    def status(request: Request):
        if not authorized(request, token):
            return JSONResponse({'error': 'POLICY_AUTH_REQUIRED'}, status_code=401)
        return store.status()

    @router.patch('/internal/secure-control/roi-policy')
    async def apply(request: Request):
        request_id = uuid4().hex
        if not authorized(request, token):
            return JSONResponse({'error': 'POLICY_AUTH_REQUIRED'}, status_code=401)
        try:
            raw = bytearray()
            async with asyncio.timeout(1):
                async for chunk in request.stream():
                    raw.extend(chunk)
                    if len(raw) > 16384:
                        raise RoiError('INVALID_POLICY')
            body = decode(raw)
            if (not {'expectedRevision', 'configuredRevision'} <= set(body)
                    or set(body)-{'expectedRevision', 'configuredRevision', 'changes'}):
                raise RoiError('INVALID_POLICY')
            result = await asyncio.to_thread(store.apply, body['expectedRevision'], body.get('changes'),
                                             configured_revision=body['configuredRevision'])
            return dict(result, requestId=request_id)
        except RoiError as exc:
            return JSONResponse(exc.payload(request_id), status_code=409 if 'CONFLICT' in exc.code else 422)
        except (ControlError, TimeoutError):
            return JSONResponse(RoiError('INVALID_POLICY').payload(request_id), status_code=422)
        except OSError:
            return JSONResponse(RoiError('POLICY_PERSISTENCE_FAILED').payload(request_id), status_code=503)

    return router
