"""User config handler class using constructor dependency injection.

All handlers use the new ApiHandler pattern: typed parameters
(``BodyParam``, ``QueryParam``, ``UserContext``, ``RequestCtx``) are
automatically extracted by ``_wrap_api_handler`` and responses are
returned as ``APIResponse`` objects.
"""

from __future__ import annotations

import logging
from http import HTTPStatus
from typing import Final

from ai.backend.common.api_handlers import APIResponse, BodyParam, QueryParam
from ai.backend.common.dto.manager.config.request import (
    CreateUserDotfileRequest,
    DeleteUserDotfileRequest,
    GetUserDotfileRequest,
    UpdateBootstrapScriptRequest,
    UpdateUserDotfileRequest,
)
from ai.backend.common.dto.manager.config.response import (
    CreateDotfileResponse,
    DeleteDotfileResponse,
    DotfileItem,
    GetBootstrapScriptResponse,
    GetDotfileResponse,
    ListDotfilesResponse,
    UpdateBootstrapScriptResponse,
    UpdateDotfileResponse,
)
from ai.backend.logging import BraceStyleAdapter
from ai.backend.manager.data.dotfile.types import DotfileScope
from ai.backend.manager.dto.context import UserContext
from ai.backend.manager.services.auth.actions.resolve_access_key_scope import (
    ResolveAccessKeyScopeAction,
)
from ai.backend.manager.services.auth.processors import AuthProcessors
from ai.backend.manager.services.dotfile import (
    CreateDotfileAction,
    DeleteDotfileAction,
    GetBootstrapScriptAction,
    ListOrGetDotfilesAction,
    UpdateBootstrapScriptAction,
    UpdateDotfileAction,
)
from ai.backend.manager.services.dotfile.processors import DotfileProcessors

log: Final = BraceStyleAdapter(logging.getLogger(__spec__.name))


class UserConfigHandler:
    """User config (dotfile) API handler with constructor-injected dependencies."""

    def __init__(self, *, auth: AuthProcessors, dotfile: DotfileProcessors) -> None:
        self._auth = auth
        self._dotfile = dotfile

    async def create(
        self,
        body: BodyParam[CreateUserDotfileRequest],
        ctx: UserContext,
    ) -> APIResponse:
        params = body.parsed
        scope = await self._auth.resolve_access_key_scope.wait_for_complete(
            ResolveAccessKeyScopeAction(
                requester_access_key=ctx.access_key,
                requester_role=ctx.user_role,
                requester_domain=ctx.user_domain,
                owner_access_key=params.owner_access_key,
            )
        )
        requester_access_key, owner_access_key = (
            scope.requester_access_key,
            scope.owner_access_key,
        )
        log.info(
            "USERCONFIG.CREATE(ak:{}/{})",
            requester_access_key,
            owner_access_key if owner_access_key != requester_access_key else "*",
        )
        action = CreateDotfileAction(
            scope=DotfileScope.USER,
            entity_key=owner_access_key,
            path=params.path,
            data=params.data,
            permission=params.permission,
            user_uuid=ctx.user_uuid,
        )
        await self._dotfile.create.wait_for_complete(action)
        return APIResponse.build(HTTPStatus.OK, CreateDotfileResponse())

    async def list_or_get(
        self,
        query: QueryParam[GetUserDotfileRequest],
        ctx: UserContext,
    ) -> APIResponse:
        params = query.parsed
        scope = await self._auth.resolve_access_key_scope.wait_for_complete(
            ResolveAccessKeyScopeAction(
                requester_access_key=ctx.access_key,
                requester_role=ctx.user_role,
                requester_domain=ctx.user_domain,
                owner_access_key=params.owner_access_key,
            )
        )
        requester_access_key, owner_access_key = (
            scope.requester_access_key,
            scope.owner_access_key,
        )
        log.info(
            "USERCONFIG.LIST_OR_GET(ak:{}/{})",
            requester_access_key,
            owner_access_key if owner_access_key != requester_access_key else "*",
        )
        action = ListOrGetDotfilesAction(
            scope=DotfileScope.USER,
            entity_key=owner_access_key,
            path=params.path,
        )
        result = await self._dotfile.list_or_get.wait_for_complete(action)
        if params.path:
            entry = result.entries[0]
            return APIResponse.build(
                HTTPStatus.OK,
                GetDotfileResponse(path=entry.path, perm=entry.perm, data=entry.data),
            )
        items = [DotfileItem(path=e.path, perm=e.perm, data=e.data) for e in result.entries]
        return APIResponse.build(HTTPStatus.OK, ListDotfilesResponse(items=items))

    async def update(
        self,
        body: BodyParam[UpdateUserDotfileRequest],
        ctx: UserContext,
    ) -> APIResponse:
        params = body.parsed
        scope = await self._auth.resolve_access_key_scope.wait_for_complete(
            ResolveAccessKeyScopeAction(
                requester_access_key=ctx.access_key,
                requester_role=ctx.user_role,
                requester_domain=ctx.user_domain,
                owner_access_key=params.owner_access_key,
            )
        )
        requester_access_key, owner_access_key = (
            scope.requester_access_key,
            scope.owner_access_key,
        )
        log.info(
            "USERCONFIG.UPDATE(ak:{}/{})",
            requester_access_key,
            owner_access_key if owner_access_key != requester_access_key else "*",
        )
        action = UpdateDotfileAction(
            scope=DotfileScope.USER,
            entity_key=owner_access_key,
            path=params.path,
            data=params.data,
            permission=params.permission,
        )
        await self._dotfile.update.wait_for_complete(action)
        return APIResponse.build(HTTPStatus.OK, UpdateDotfileResponse())

    async def delete(
        self,
        query: QueryParam[DeleteUserDotfileRequest],
        ctx: UserContext,
    ) -> APIResponse:
        params = query.parsed
        scope = await self._auth.resolve_access_key_scope.wait_for_complete(
            ResolveAccessKeyScopeAction(
                requester_access_key=ctx.access_key,
                requester_role=ctx.user_role,
                requester_domain=ctx.user_domain,
                owner_access_key=params.owner_access_key,
            )
        )
        requester_access_key, owner_access_key = (
            scope.requester_access_key,
            scope.owner_access_key,
        )
        log.info(
            "USERCONFIG.DELETE(ak:{}/{})",
            requester_access_key,
            owner_access_key if owner_access_key != requester_access_key else "*",
        )
        action = DeleteDotfileAction(
            scope=DotfileScope.USER,
            entity_key=owner_access_key,
            path=params.path,
        )
        await self._dotfile.delete.wait_for_complete(action)
        return APIResponse.build(HTTPStatus.OK, DeleteDotfileResponse(success=True))

    async def update_bootstrap_script(
        self,
        body: BodyParam[UpdateBootstrapScriptRequest],
        ctx: UserContext,
    ) -> APIResponse:
        params = body.parsed
        log.info("USERCONFIG.UPDATE_BOOTSTRAP_SCRIPT(ak:{})", ctx.access_key)
        action = UpdateBootstrapScriptAction(
            access_key=ctx.access_key,
            script=params.script,
        )
        await self._dotfile.update_bootstrap.wait_for_complete(action)
        return APIResponse.build(HTTPStatus.OK, UpdateBootstrapScriptResponse())

    async def get_bootstrap_script(
        self,
        ctx: UserContext,
    ) -> APIResponse:
        log.info("USERCONFIG.GET_BOOTSTRAP_SCRIPT(ak:{})", ctx.access_key)
        action = GetBootstrapScriptAction(access_key=ctx.access_key)
        result = await self._dotfile.get_bootstrap.wait_for_complete(action)
        return APIResponse.build(
            HTTPStatus.OK,
            GetBootstrapScriptResponse(script=result.script),
        )
