from __future__ import annotations

from typing import Annotated, NoReturn

from fastapi import APIRouter, Depends, Header, HTTPException, status
from sqlalchemy.orm import Session

from app.api.deps import CurrentUserContext, get_db, require_rule_reviewer_user
from app.schemas.agent_asset_release import (
    AgentAssetReleaseMonitorRead,
    AgentAssetReleaseMonitorTriggerWrite,
    AgentAssetReleaseReviewLabelRead,
    AgentAssetReleaseReviewLabelWrite,
    AgentAssetReleaseReviewQueueRead,
    AgentAssetReleaseRollbackWrite,
    AgentAssetReleaseServingPlanRead,
    AgentAssetReleaseStartWrite,
    AgentAssetReleaseStateRead,
)
from app.services.agent_asset_access import stable_user_principal
from app.services.agent_asset_release_guard import (
    AgentAssetReleaseGuardService,
    ReleaseGuardPolicy,
)
from app.services.agent_asset_release_monitor import AgentAssetReleaseMonitor
from app.services.agent_asset_release_monitor_auth import (
    ReleaseMonitorAuthenticationError,
    ReleaseMonitorConfigurationError,
    require_release_monitor_signature,
)
from app.services.agent_asset_release_review import AgentAssetReleaseReviewService

router = APIRouter(prefix="/agent-assets")
DbSession = Annotated[Session, Depends(get_db)]
RuleReviewerUser = Annotated[CurrentUserContext, Depends(require_rule_reviewer_user)]
RequestIdHeader = Annotated[
    str | None,
    Header(description="外部请求 ID，用于串联审计日志和上游调用链。"),
]
ReleaseMonitorTimestampHeader = Annotated[
    str | None,
    Header(alias="X-Release-Monitor-Timestamp"),
]
ReleaseMonitorSignatureHeader = Annotated[
    str | None,
    Header(alias="X-Release-Monitor-Signature"),
]


def _handle_error(exc: Exception) -> NoReturn:
    if isinstance(exc, LookupError):
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
    if isinstance(exc, (PermissionError, ValueError)):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
    raise exc


def _actor(user: CurrentUserContext) -> str:
    return stable_user_principal(user)


def _state_payload(value: dict) -> AgentAssetReleaseStateRead:
    if not value:
        return AgentAssetReleaseStateRead()
    allowed = set(AgentAssetReleaseStateRead.model_fields)
    return AgentAssetReleaseStateRead.model_validate(
        {key: item for key, item in value.items() if key in allowed}
    )


@router.get(
    "/{asset_id}/release",
    response_model=AgentAssetReleaseStateRead,
    summary="查询 Agent 资产分阶段发布状态",
)
def get_agent_asset_release(
    asset_id: str,
    current_user: RuleReviewerUser,
    db: DbSession,
) -> AgentAssetReleaseStateRead:
    try:
        return _state_payload(
            AgentAssetReleaseGuardService(db).get_state(
                asset_id,
                tenant_id=current_user.tenant_id,
                allow_global_management=current_user.is_admin,
            )
        )
    except Exception as exc:
        _handle_error(exc)


@router.get(
    "/{asset_id}/release/serving-plan",
    response_model=AgentAssetReleaseServingPlanRead,
    summary="查询运行时版本路由计划",
)
def get_agent_asset_release_serving_plan(
    asset_id: str,
    current_user: RuleReviewerUser,
    db: DbSession,
) -> AgentAssetReleaseServingPlanRead:
    try:
        return AgentAssetReleaseServingPlanRead.model_validate(
            AgentAssetReleaseGuardService(db).get_serving_plan(
                asset_id,
                tenant_id=current_user.tenant_id,
                allow_global_management=current_user.is_admin,
            )
        )
    except Exception as exc:
        _handle_error(exc)


@router.post(
    "/{asset_id}/release/shadow",
    response_model=AgentAssetReleaseStateRead,
    summary="启动受控影子发布",
)
def start_agent_asset_shadow_release(
    asset_id: str,
    payload: AgentAssetReleaseStartWrite,
    current_user: RuleReviewerUser,
    db: DbSession,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetReleaseStateRead:
    try:
        policy = ReleaseGuardPolicy(**payload.policy.model_dump())
        value = AgentAssetReleaseGuardService(db).start_shadow(
            asset_id,
            payload.candidate_version,
            actor=_actor(current_user),
            policy=policy,
            tenant_id=current_user.tenant_id,
            allow_global_management=current_user.is_admin,
            request_id=x_request_id,
        )
        return _state_payload(value)
    except Exception as exc:
        _handle_error(exc)


@router.post(
    "/{asset_id}/release/evaluations",
    response_model=AgentAssetReleaseMonitorRead,
    summary="聚合真实发布遥测并在越界时自动回滚",
)
def evaluate_agent_asset_release(
    asset_id: str,
    payload: AgentAssetReleaseMonitorTriggerWrite,
    current_user: RuleReviewerUser,
    db: DbSession,
    x_release_monitor_timestamp: ReleaseMonitorTimestampHeader = None,
    x_release_monitor_signature: ReleaseMonitorSignatureHeader = None,
) -> AgentAssetReleaseMonitorRead:
    try:
        service = AgentAssetReleaseGuardService(db)
        state = service.get_state(
            asset_id,
            tenant_id=current_user.tenant_id,
            allow_global_management=current_user.is_admin,
        )
        require_release_monitor_signature(
            timestamp=x_release_monitor_timestamp,
            signature=x_release_monitor_signature,
            tenant_id=current_user.tenant_id,
            asset_id=asset_id,
            release_id=str(state.get("release_id") or ""),
            stage=str(state.get("stage") or ""),
            payload=payload.model_dump(mode="json", exclude_unset=True),
        )
        value = AgentAssetReleaseMonitor(db).evaluate_current(
            asset_id=asset_id,
            actor=_actor(current_user),
            tenant_id=current_user.tenant_id,
            allow_global_management=current_user.is_admin,
        )
        return AgentAssetReleaseMonitorRead.model_validate(value)
    except ReleaseMonitorConfigurationError as exc:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail=str(exc),
        ) from exc
    except ReleaseMonitorAuthenticationError as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=str(exc),
        ) from exc
    except Exception as exc:
        _handle_error(exc)


@router.get(
    "/{asset_id}/release/review-queue",
    response_model=AgentAssetReleaseReviewQueueRead,
    summary="查询当前发布的去敏人工复核队列",
)
def list_agent_asset_release_review_queue(
    asset_id: str,
    current_user: RuleReviewerUser,
    db: DbSession,
    limit: int = 50,
) -> AgentAssetReleaseReviewQueueRead:
    try:
        value = AgentAssetReleaseReviewService(db).list_pending(
            tenant_id=current_user.tenant_id,
            asset_id=asset_id,
            limit=limit,
        )
        return AgentAssetReleaseReviewQueueRead.model_validate(value)
    except Exception as exc:
        _handle_error(exc)


@router.post(
    "/{asset_id}/release/review-queue/{observation_id}/labels",
    response_model=AgentAssetReleaseReviewLabelRead,
    summary="提交类型化发布复核结论并重新聚合",
)
def label_agent_asset_release_observation(
    asset_id: str,
    observation_id: str,
    payload: AgentAssetReleaseReviewLabelWrite,
    current_user: RuleReviewerUser,
    db: DbSession,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetReleaseReviewLabelRead:
    try:
        request_id = str(x_request_id or "").strip()
        if not request_id:
            raise ValueError("X-Request-Id is required for release review labels.")
        label = AgentAssetReleaseReviewService(db).record_label(
            tenant_id=current_user.tenant_id,
            asset_id=asset_id,
            observation_id=observation_id,
            label=payload.label,
            actor_id=_actor(current_user),
            request_id=request_id,
        )
        db.commit()
        monitor = AgentAssetReleaseMonitor(db).evaluate_current(
            asset_id=asset_id,
            actor=_actor(current_user),
            tenant_id=current_user.tenant_id,
        )
        return AgentAssetReleaseReviewLabelRead.model_validate(
            {
                "label_id": label.id,
                "observation_id": label.observation_id,
                "label": label.label,
                "monitor": monitor,
            }
        )
    except Exception as exc:
        db.rollback()
        _handle_error(exc)


@router.post(
    "/{asset_id}/release/promote",
    response_model=AgentAssetReleaseStateRead,
    summary="将通过评测的版本晋级到下一阶段",
)
def promote_agent_asset_release(
    asset_id: str,
    current_user: RuleReviewerUser,
    db: DbSession,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetReleaseStateRead:
    try:
        return _state_payload(
            AgentAssetReleaseGuardService(db).promote(
                asset_id,
                actor=_actor(current_user),
                tenant_id=current_user.tenant_id,
                allow_global_management=current_user.is_admin,
                request_id=x_request_id,
            )
        )
    except Exception as exc:
        _handle_error(exc)


@router.post(
    "/{asset_id}/release/rollback",
    response_model=AgentAssetReleaseStateRead,
    summary="显式回滚到发布前版本",
)
def rollback_agent_asset_release(
    asset_id: str,
    payload: AgentAssetReleaseRollbackWrite,
    current_user: RuleReviewerUser,
    db: DbSession,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetReleaseStateRead:
    try:
        return _state_payload(
            AgentAssetReleaseGuardService(db).rollback(
                asset_id,
                actor=_actor(current_user),
                reason=payload.reason,
                tenant_id=current_user.tenant_id,
                allow_global_management=current_user.is_admin,
                request_id=x_request_id,
            )
        )
    except Exception as exc:
        _handle_error(exc)
