from __future__ import annotations

from typing import Annotated

from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, status
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session

from app.api.deps import (
    CurrentUserContext,
    get_current_user,
    get_db,
    require_platform_admin_user,
    require_rule_editor_user,
    require_rule_reviewer_user,
)
from app.api.pagination import PageNumber, PageSize, page_payload, wants_page
from app.schemas.agent_asset import (
    AgentAssetCreate,
    AgentAssetListItem,
    AgentAssetOnlyOfficeCallbackRead,
    AgentAssetOnlyOfficeCallbackWrite,
    AgentAssetOnlyOfficeConfigRead,
    AgentAssetRead,
    AgentAssetReviewCreate,
    AgentAssetReviewRead,
    AgentAssetSpreadsheetChangeRecordRead,
    AgentAssetUpdate,
    AgentAssetVersionCreate,
    AgentAssetVersionRead,
    AgentAssetVersionTimelineItemRead,
    GoldenCaseCreate,
    GoldenCaseRead,
    GoldenEvalRead,
    GoldenEvalRequest,
)
from app.schemas.common import ErrorResponse, PaginatedResponse
from app.services.agent_asset_access import stable_user_principal
from app.services.agent_asset_onlyoffice_security import (
    AGENT_ASSET_ONLYOFFICE_CALLBACK_SCOPE,
    AgentAssetOnlyOfficeReplayError,
    AgentAssetOnlyOfficeSecurityError,
    AgentAssetOnlyOfficeValidatedSession,
)
from app.services.agent_assets import AgentAssetService

router = APIRouter(prefix="/agent-assets")
DbSession = Annotated[Session, Depends(get_db)]
ActorHeader = Annotated[
    str | None,
    Header(description="兼容旧客户端；审计主体始终以登录会话中的稳定身份为准。"),
]
RequestIdHeader = Annotated[
    str | None,
    Header(description="外部请求 ID，用于串联审计日志和上游调用链。"),
]
CurrentUser = Annotated[CurrentUserContext, Depends(get_current_user)]
PlatformAdminUser = Annotated[CurrentUserContext, Depends(require_platform_admin_user)]
RuleEditorUser = Annotated[CurrentUserContext, Depends(require_rule_editor_user)]
RuleReviewerUser = Annotated[CurrentUserContext, Depends(require_rule_reviewer_user)]


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


def _onlyoffice_service_for_session(
    db: Session,
    session: AgentAssetOnlyOfficeValidatedSession,
) -> AgentAssetService:
    machine_user = CurrentUserContext(
        username=session.actor,
        name="ONLYOFFICE",
        role_codes=["manager"],
        is_admin=session.resource_scope == "platform",
        tenant_id=session.tenant_id,
    )
    return AgentAssetService(db, current_user=machine_user)


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


@router.get(
    "",
    response_model=list[AgentAssetListItem] | PaginatedResponse[AgentAssetListItem],
    summary="查询 Agent 资产列表",
    description="按资产类型、状态、领域和关键字筛选规则、技能、MCP 与任务资产。",
)
def list_agent_assets(
    current_user: CurrentUser,
    db: DbSession,
    asset_type: Annotated[
        str | None,
        Query(description="资产类型：`rule`、`skill`、`mcp`、`task`。"),
    ] = None,
    status_value: Annotated[
        str | None,
        Query(alias="status", description="资产状态筛选。"),
    ] = None,
    domain: Annotated[
        str | None,
        Query(description="业务领域筛选，例如 `expense`、`ar`、`ap`。"),
    ] = None,
    keyword: Annotated[
        str | None,
        Query(description="资产编码、名称关键字模糊查询。"),
    ] = None,
    page: PageNumber = None,
    page_size: PageSize = None,
) -> list[AgentAssetListItem] | PaginatedResponse[AgentAssetListItem]:
    service = AgentAssetService(db, current_user=current_user)
    if wants_page(page, page_size):
        return page_payload(
            service.list_assets_page(
                asset_type=asset_type,
                status=status_value,
                domain=domain,
                keyword=keyword,
                page=page,
                page_size=page_size,
            )
        )
    return service.list_assets(
        asset_type=asset_type,
        status=status_value,
        domain=domain,
        keyword=keyword,
    )


@router.get(
    "/{asset_id}",
    response_model=AgentAssetRead,
    summary="读取 Agent 资产详情",
    description="返回资产当前版本正文、最近版本列表和最近一次审核信息。",
    responses={
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "资产不存在。",
        }
    },
)
def get_agent_asset(
    asset_id: str,
    current_user: CurrentUser,
    db: DbSession,
) -> AgentAssetRead:
    asset = AgentAssetService(db, current_user=current_user).get_asset(asset_id)
    if asset is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Asset not found")
    return asset


@router.get(
    "/{asset_id}/spreadsheet/onlyoffice-config",
    response_model=AgentAssetOnlyOfficeConfigRead,
    summary="读取规则 Excel 的 ONLYOFFICE 配置",
    description="为规则详情页中的 Excel 规则表生成 ONLYOFFICE 配置。",
)
def get_agent_asset_spreadsheet_onlyoffice_config(
    asset_id: str,
    current_user: CurrentUser,
    db: DbSession,
    version: Annotated[
        str | None,
        Query(description="兼容旧前端的可选参数；表格规则始终打开当前规则表。"),
    ] = None,
) -> AgentAssetOnlyOfficeConfigRead:
    try:
        return AgentAssetService(
            db, current_user=current_user
        ).build_rule_spreadsheet_onlyoffice_config(
            asset_id,
            current_user,
            version=version,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.get(
    "/{asset_id}/spreadsheet/content",
    response_class=FileResponse,
    summary="下载或预览规则 Excel 文件",
    description="返回当前规则 Excel 文件，用于浏览器预览或下载。",
)
def get_agent_asset_spreadsheet_content(
    asset_id: str,
    current_user: CurrentUser,
    db: DbSession,
    version: Annotated[
        str | None,
        Query(description="兼容旧前端的可选参数；不传时返回当前规则表。"),
    ] = None,
) -> FileResponse:
    try:
        file_path, media_type, filename = AgentAssetService(
            db, current_user=current_user
        ).get_rule_spreadsheet_content(asset_id, version=version)
    except Exception as exc:
        _handle_asset_error(exc)

    return FileResponse(file_path, media_type=media_type, filename=filename)


@router.get(
    "/{asset_id}/spreadsheet/onlyoffice/content",
    response_class=FileResponse,
    summary="供 ONLYOFFICE 读取规则 Excel 源文件",
    description="使用短时令牌供 ONLYOFFICE 拉取规则表源文件。",
)
def get_agent_asset_spreadsheet_onlyoffice_content(
    asset_id: str,
    db: DbSession,
    access_token: Annotated[
        str,
        Query(min_length=1, description="ONLYOFFICE 临时访问令牌。"),
    ],
    version: Annotated[
        str | None,
        Query(description="兼容旧 ONLYOFFICE URL；当前表格模式不再使用。"),
    ] = None,
) -> FileResponse:
    try:
        bootstrap_service = AgentAssetService(db)
        validated_session = bootstrap_service.validate_rule_spreadsheet_access_token(
            asset_id, access_token
        )
        service = _onlyoffice_service_for_session(db, validated_session)
        file_path, media_type, filename = service.get_rule_spreadsheet_content(
            asset_id,
            version=version,
            validated_session=validated_session,
        )
    except FileNotFoundError as exc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
    except AgentAssetOnlyOfficeSecurityError as exc:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
    except Exception as exc:
        _handle_asset_error(exc)

    return FileResponse(file_path, media_type=media_type, filename=filename)


@router.post(
    "/{asset_id}/spreadsheet/upload",
    response_model=AgentAssetRead,
    status_code=status.HTTP_201_CREATED,
    summary="上传规则 Excel 文件",
    description="为指定规则上传新的 Excel 文件，并记录本次表格修改。",
)
def upload_agent_asset_spreadsheet(
    asset_id: str,
    content: Annotated[
        bytes,
        Body(
            media_type="application/octet-stream",
            description="待上传的 Excel 文件二进制内容。",
        ),
    ],
    filename: Annotated[str, Query(min_length=1, description="原始文件名。")],
    current_user: RuleEditorUser,
    db: DbSession,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetRead:
    try:
        return AgentAssetService(db, current_user=current_user).upload_rule_spreadsheet(
            asset_id,
            filename=filename,
            content=content,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.post(
    "/{asset_id}/spreadsheet/import-content",
    response_model=AgentAssetRead,
    status_code=status.HTTP_201_CREATED,
    summary="导入规则 Excel 表格内容",
    description="读取上传 Excel 中的工作表内容，写回当前规则表；保留当前规则文件名与规则身份。",
)
def import_agent_asset_spreadsheet_content(
    asset_id: str,
    content: Annotated[
        bytes,
        Body(
            media_type="application/octet-stream",
            description="待导入的 Excel 文件二进制内容。",
        ),
    ],
    filename: Annotated[str, Query(min_length=1, description="上传文件原始文件名。")],
    current_user: RuleEditorUser,
    db: DbSession,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetRead:
    try:
        return AgentAssetService(
            db, current_user=current_user
        ).import_rule_spreadsheet_content(
            asset_id,
            filename=filename,
            content=content,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.post(
    "/{asset_id}/spreadsheet/onlyoffice/callback",
    response_model=AgentAssetOnlyOfficeCallbackRead,
    summary="接收规则 Excel 的 ONLYOFFICE 回调",
    description="接收 ONLYOFFICE 回写内容，并记录本次表格修改。",
)
def handle_agent_asset_spreadsheet_onlyoffice_callback(
    asset_id: str,
    payload: AgentAssetOnlyOfficeCallbackWrite,
    db: DbSession,
    access_token: Annotated[
        str,
        Query(min_length=1, description="ONLYOFFICE 回调专用短时令牌。"),
    ],
    version: Annotated[
        str | None,
        Query(description="兼容旧 ONLYOFFICE 回调；当前表格模式不再使用。"),
    ] = None,
) -> AgentAssetOnlyOfficeCallbackRead:
    try:
        bootstrap_service = AgentAssetService(db)
        validated_session = bootstrap_service.validate_rule_spreadsheet_access_token(
            asset_id,
            access_token,
            expected_scope=AGENT_ASSET_ONLYOFFICE_CALLBACK_SCOPE,
        )
        service = _onlyoffice_service_for_session(db, validated_session)
        service.handle_rule_spreadsheet_onlyoffice_callback(
            asset_id,
            version=version,
            payload=payload.model_dump(),
            callback_token=access_token,
        )
    except AgentAssetOnlyOfficeReplayError as exc:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(exc)) from exc
    except AgentAssetOnlyOfficeSecurityError as exc:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
    except Exception as exc:
        _handle_asset_error(exc)

    return AgentAssetOnlyOfficeCallbackRead()


@router.get(
    "/{asset_id}/spreadsheet/change-records",
    response_model=list[AgentAssetSpreadsheetChangeRecordRead],
    summary="读取规则表最近修改记录",
    description="返回最近 30 次 ONLYOFFICE 保存级修改记录，用于展示操作者、时间和具体差异。",
)
def list_agent_asset_spreadsheet_change_records(
    asset_id: str,
    current_user: CurrentUser,
    db: DbSession,
    limit: Annotated[int, Query(ge=1, le=30, description="返回条数，最多 30 条。")] = 30,
) -> list[AgentAssetSpreadsheetChangeRecordRead]:
    try:
        return AgentAssetService(
            db, current_user=current_user
        ).list_spreadsheet_change_records(asset_id, limit=limit)
    except Exception as exc:
        _handle_asset_error(exc)


@router.post(
    "",
    response_model=AgentAssetRead,
    status_code=status.HTTP_201_CREATED,
    summary="创建 Agent 资产",
    description="创建新的规则、技能、MCP 或任务资产，并自动记录审计日志。",
    responses={
        status.HTTP_400_BAD_REQUEST: {
            "model": ErrorResponse,
            "description": "资产编码冲突或请求字段不合法。",
        }
    },
)
def create_agent_asset(
    payload: AgentAssetCreate,
    current_user: RuleReviewerUser,
    db: DbSession,
    x_actor: ActorHeader = None,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetRead:
    try:
        return AgentAssetService(db, current_user=current_user).create_asset(
            payload,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.patch(
    "/{asset_id}",
    response_model=AgentAssetRead,
    summary="更新 Agent 资产",
    description="更新资产基础信息、当前版本、状态和配置，并写入审计日志。",
    responses={
        status.HTTP_400_BAD_REQUEST: {
            "model": ErrorResponse,
            "description": "状态更新非法或请求字段不合法。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "资产或指定版本不存在。",
        },
    },
)
def update_agent_asset(
    asset_id: str,
    payload: AgentAssetUpdate,
    current_user: RuleEditorUser,
    db: DbSession,
    x_actor: ActorHeader = None,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetRead:
    try:
        role_codes = {item.strip() for item in current_user.role_codes}
        if (payload.status is not None or payload.published_version is not None) and not (
            current_user.is_admin or "manager" in role_codes
        ):
            raise PermissionError("只有高级管理员或 admin 管理员可以更改规则上线状态。")
        return AgentAssetService(db, current_user=current_user).update_asset(
            asset_id,
            payload,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.get(
    "/{asset_id}/versions",
    response_model=list[AgentAssetVersionRead],
    summary="查询资产版本列表",
    description="返回指定资产的版本历史，默认按最近版本优先排序。",
    responses={
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "资产不存在。",
        }
    },
)
def list_agent_asset_versions(
    asset_id: str,
    current_user: CurrentUser,
    db: DbSession,
    limit: Annotated[
        int,
        Query(ge=1, le=100, description="返回版本数量上限。"),
    ] = 20,
) -> list[AgentAssetVersionRead]:
    try:
        return AgentAssetService(db, current_user=current_user).list_versions(
            asset_id, limit=limit
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.post(
    "/{asset_id}/versions",
    response_model=AgentAssetVersionRead,
    status_code=status.HTTP_201_CREATED,
    summary="创建资产版本",
    description=(
        "为指定资产创建新版本；规则和任务源文件可使用 Markdown，技能与 MCP 使用 JSON 快照。"
    ),
    responses={
        status.HTTP_400_BAD_REQUEST: {
            "model": ErrorResponse,
            "description": "版本号重复或内容类型不匹配。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "资产不存在。",
        },
    },
)
def create_agent_asset_version(
    asset_id: str,
    payload: AgentAssetVersionCreate,
    current_user: RuleEditorUser,
    db: DbSession,
    x_actor: ActorHeader = None,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetVersionRead:
    try:
        payload = payload.model_copy(update={"created_by": _actor(current_user)})
        return AgentAssetService(db, current_user=current_user).create_version(
            asset_id,
            payload,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.post(
    "/{asset_id}/reviews",
    response_model=AgentAssetReviewRead,
    status_code=status.HTTP_201_CREATED,
    summary="创建资产审核记录",
    description="为指定资产版本写入审核结果，并联动更新资产状态。",
    responses={
        status.HTTP_400_BAD_REQUEST: {
            "model": ErrorResponse,
            "description": "审核参数不合法。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "资产或版本不存在。",
        },
    },
)
def create_agent_asset_review(
    asset_id: str,
    payload: AgentAssetReviewCreate,
    current_user: CurrentUser,
    db: DbSession,
    x_actor: ActorHeader = None,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetReviewRead:
    try:
        role_codes = {item.strip() for item in current_user.role_codes}
        if payload.review_status.value == "pending":
            if not (current_user.is_admin or "manager" in role_codes or "finance" in role_codes):
                raise PermissionError("只有财务人员或高级财务人员可以提交审核。")
        elif not (current_user.is_admin or "manager" in role_codes):
            raise PermissionError("只有高级财务人员可以审核规则。")
        payload = payload.model_copy(update={"reviewer": _actor(current_user)})
        return AgentAssetService(db, current_user=current_user).create_review(
            asset_id,
            payload,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.post(
    "/{asset_id}/activate",
    response_model=AgentAssetRead,
    summary="激活资产当前版本",
    description="将资产当前版本切换为上线状态；规则资产必须已有 `approved` 审核记录。",
    responses={
        status.HTTP_400_BAD_REQUEST: {
            "model": ErrorResponse,
            "description": "审核未通过或当前版本未设置。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "资产不存在。",
        },
    },
)
def activate_agent_asset(
    asset_id: str,
    current_user: RuleReviewerUser,
    db: DbSession,
    x_actor: ActorHeader = None,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetRead:
    try:
        return AgentAssetService(db, current_user=current_user).activate_asset(
            asset_id,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.delete(
    "/{asset_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="删除未发布风险规则",
    description="仅允许删除从未发布过的 JSON 风险规则，并同步删除规则 JSON 文件。",
)
def delete_agent_asset(
    asset_id: str,
    current_user: PlatformAdminUser,
    db: DbSession,
    x_actor: ActorHeader = None,
    x_request_id: RequestIdHeader = None,
) -> None:
    try:
        AgentAssetService(db, current_user=current_user).delete_unpublished_asset(
            asset_id,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.post(
    "/{asset_id}/versions/{version}/restore",
    response_model=AgentAssetRead,
    summary="基于历史版本恢复工作稿",
    description="复制指定历史版本内容生成新的工作版本，用于误上线后的快速恢复与重新审核。",
)
def restore_agent_asset_version(
    asset_id: str,
    version: str,
    current_user: RuleReviewerUser,
    db: DbSession,
    x_actor: ActorHeader = None,
    x_request_id: RequestIdHeader = None,
) -> AgentAssetRead:
    try:
        return AgentAssetService(
            db, current_user=current_user
        ).restore_version_as_working_copy(
            asset_id,
            version,
            actor=_actor(current_user),
            request_id=x_request_id,
        )
    except Exception as exc:
        _handle_asset_error(exc)


@router.get(
    "/{asset_id}/version-timeline",
    response_model=list[AgentAssetVersionTimelineItemRead],
    summary="读取规则版本流转时间线",
    description="返回规则版本创建、提交审核、审核结果和正式上线等流转事件。",
)
def get_agent_asset_version_timeline(
    asset_id: str,
    current_user: CurrentUser,
    db: DbSession,
) -> list[AgentAssetVersionTimelineItemRead]:
    try:
        return AgentAssetService(db, current_user=current_user).list_version_timeline(asset_id)
    except Exception as exc:
        _handle_asset_error(exc)


@router.post(
    "/risk-rules/golden-cases",
    response_model=GoldenCaseRead,
    status_code=status.HTTP_201_CREATED,
    summary="创建 golden set 黄金用例",
    description="为指定规则（或通用场景）创建一条回归用例，发布前作为门禁集执行。",
)
def create_golden_case(
    body: GoldenCaseCreate,
    _: RuleEditorUser,
    db: DbSession,
) -> GoldenCaseRead:
    from sqlalchemy import select

    from app.models.golden_case import GoldenCase

    existing = db.scalar(select(GoldenCase).where(GoldenCase.case_key == body.case_key))
    if existing is not None:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="case_key 已存在")
    case = GoldenCase(
        case_key=body.case_key,
        rule_code=body.rule_code,
        scene=body.scene,
        name=body.name,
        values_json=body.values,
        expected_hit=body.expected_hit,
        expected_severity=body.expected_severity,
        note=body.note,
        status="active",
        source="manual",
    )
    db.add(case)
    db.commit()
    db.refresh(case)
    return _golden_case_read(case)


@router.get(
    "/risk-rules/{rule_code}/golden-cases",
    response_model=list[GoldenCaseRead],
    summary="列出规则的 golden 用例",
)
def list_golden_cases(
    rule_code: str,
    _: CurrentUser,
    db: DbSession,
) -> list[GoldenCaseRead]:
    from sqlalchemy import select

    from app.models.golden_case import GoldenCase

    cases = db.scalars(
        select(GoldenCase).where(GoldenCase.rule_code == rule_code).order_by(GoldenCase.created_at)
    ).all()
    return [_golden_case_read(case) for case in cases]


@router.post(
    "/{asset_id}/golden-eval",
    response_model=GoldenEvalRead,
    summary="手动触发 golden set 评测（不入门禁）",
    description="在当前规则版本上跑 golden 用例集，返回指标。门禁由 publish 时自动触发。",
)
def run_golden_eval(
    asset_id: str,
    body: GoldenEvalRequest,
    current_user: RuleReviewerUser,
    db: DbSession,
) -> GoldenEvalRead:
    from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY
    from app.services.risk_rule_golden_evaluator import RiskRuleGoldenEvaluator

    try:
        service = AgentAssetService(db, current_user=current_user)
        asset = service.get_asset(asset_id)
        if asset is None:
            raise LookupError("Asset not found")
        config = asset.config_json if isinstance(asset.config_json, dict) else {}
        rule_document = (
            config.get("rule_document") if isinstance(config.get("rule_document"), dict) else {}
        )
        file_name = str(rule_document.get("file_name") or "").strip()
        if not file_name:
            raise ValueError("该规则没有可执行的 manifest 文件。")
        manager = service.rule_library_manager
        manifest = manager.read_rule_library_json(library=RISK_RULES_LIBRARY, file_name=file_name)
        rule_code = str(manifest.get("rule_code") or "").strip()
        if not rule_code:
            raise ValueError("manifest 缺少 rule_code。")
        report = RiskRuleGoldenEvaluator().evaluate_for_rule(db, manifest, rule_code)
        return GoldenEvalRead(**report.to_dict())
    except Exception as exc:
        _handle_asset_error(exc)


def _golden_case_read(case) -> GoldenCaseRead:
    return GoldenCaseRead(
        id=case.id,
        case_key=case.case_key,
        rule_code=case.rule_code,
        scene=case.scene or "",
        name=case.name or "",
        values=case.values_json or {},
        expected_hit=bool(case.expected_hit),
        expected_severity=case.expected_severity,
        note=case.note,
        status=case.status,
        source=case.source,
    )
