"""찍어 낸 명세와 워커가 낸 결과를 잇는다.

materialize 가 만든 명세에 대해, 이 층은 그 뒤에 오는 것들을 다룬다 — 디스패치
기록, 결과 링크, 거절, 완료 표시, 검증. 전부 이미 존재하는 명세를 전제하고,
새 명세를 만들지 않는다.
"""
from __future__ import annotations

import argparse
from datetime import datetime, timezone
from pathlib import Path

from ..invocation import (
    AgentModelAssignment,
    agent_model_assignment_from_payload,
    materialize_standalone_result,
    publish_standalone_completion,
    verify_standalone_completion,
    verify_agent_invocation,
)
from ...dispatch_state import (
    link_agent_dispatch_result,
    record_verified_agent_dispatch,
    abandon_agent_dispatch_attempt,
    reject_agent_dispatch_result,
)
from .emit import _emit
from .inputs import (
    AgentPromptCliError,
    _is_relative_to,
    _mapping,
    _project_input,
    _project_root,
    _read_json_object,
)


def _record_dispatch(args: argparse.Namespace) -> None:
    project_root = _project_root(args.project_root)
    record = record_verified_agent_dispatch(
        project_root=project_root,
        run_manifest_path=_project_input(
            project_root, args.run_manifest, "run manifest"
        ),
        metadata_path=_project_input(
            project_root, args.metadata, "agent invocation metadata"
        ),
        enforcement_mode=args.enforcement_mode,
    )
    _emit(record, args.json)


def _reject_result(args: argparse.Namespace) -> None:
    project_root = _project_root(args.project_root)
    row = reject_agent_dispatch_result(
        project_root=project_root,
        run_manifest_path=_project_input(
            project_root, args.run_manifest, "run manifest"
        ),
        dispatch_id=args.dispatch_id,
        superseded_by=args.superseded_by,
        reason=args.reason,
    )
    _emit(row, args.json)


def _abandon_attempt(args: argparse.Namespace) -> None:
    project_root = _project_root(args.project_root)
    row = abandon_agent_dispatch_attempt(
        project_root=project_root,
        run_manifest_path=_project_input(
            project_root, args.run_manifest, "run manifest"
        ),
        invocation_ref=args.invocation_ref,
        reason=args.reason,
    )
    _emit(row, args.json)


def _link_result(args: argparse.Namespace) -> None:
    project_root = _project_root(args.project_root)
    link = link_agent_dispatch_result(
        project_root=project_root,
        run_manifest_path=_project_input(
            project_root, args.run_manifest, "run manifest"
        ),
        dispatch_id=args.dispatch_id,
        result_path=_project_input(project_root, args.result, "agent result"),
    )
    _emit(link, args.json)


def _verify(args: argparse.Namespace) -> None:
    project_root = _project_root(args.project_root)
    metadata_path = _project_input(project_root, args.metadata, "metadata")
    metadata = _read_json_object(metadata_path, "metadata")
    contract_source = _mapping(metadata.get("contractSource"), "contract source")
    mode = contract_source.get("mode")
    expected_manifest: Path | None = None
    expected_assignment: AgentModelAssignment | None = None
    if mode == "run":
        if not args.run_manifest:
            raise AgentPromptCliError("run invocation verification requires --run-manifest")
        expected_manifest = _project_input(project_root, args.run_manifest, "run manifest")
        manifest = _read_json_object(expected_manifest, "run manifest")
        assignments = _mapping(manifest.get("invocationAssignments"), "run assignments")
        reference = metadata.get("assignmentRef")
        if not isinstance(reference, str) or reference not in assignments:
            raise AgentPromptCliError("assignment reference is missing from run manifest")
        expected_assignment = agent_model_assignment_from_payload(assignments[reference])
    elif mode == "standalone":
        if args.run_manifest:
            raise AgentPromptCliError("standalone invocation verification forbids --run-manifest")
    else:
        raise AgentPromptCliError("contract source mode is invalid")
    errors = verify_agent_invocation(
        metadata_path,
        project_root=project_root,
        expected_run_manifest_path=expected_manifest,
        expected_assignment=expected_assignment,
    )
    if errors:
        raise AgentPromptCliError("; ".join(errors))


def _materialize_result(args: argparse.Namespace) -> None:
    project_root = _project_root(args.project_root)
    metadata_path = _project_input(project_root, args.metadata, "metadata")
    root = project_root / ".okstra" / "agent-invocations" / args.purpose
    temporary_root = root / ".tmp"
    returned_body_path = _project_input(
        project_root,
        args.returned_body_file,
        "returned body",
    )
    if not temporary_root.is_dir() or not _is_relative_to(
        returned_body_path,
        temporary_root.resolve(strict=True),
    ):
        raise AgentPromptCliError(
            "returned body file must be inside the standalone purpose .tmp directory"
        )
    result = materialize_standalone_result(
        project_root=project_root,
        purpose=args.purpose,
        metadata_path=metadata_path,
        returned_body=returned_body_path.read_bytes(),
    )
    _emit({
        "ok": True,
        "purpose": result.purpose,
        "invocationId": result.invocation_id,
        "metadataPath": str(result.prompt_metadata_path),
        "resultPath": str(result.result_path),
        "resultEnvelopeDigest": result.result_envelope_digest,
    }, args.json)


def _complete(args: argparse.Namespace) -> None:
    project_root = _project_root(args.project_root)
    metadata_path = _project_input(project_root, args.metadata, "metadata")
    completion = publish_standalone_completion(
        project_root=project_root,
        purpose=args.purpose,
        metadata_path=metadata_path,
        completed_at=datetime.now(timezone.utc),
    )
    _emit({
        "ok": True,
        "purpose": args.purpose,
        "metadataPath": str(metadata_path),
        "completionPath": str(completion),
    }, args.json)


def _verify_completion(args: argparse.Namespace) -> None:
    project_root = _project_root(args.project_root)
    completion_path = _project_input(
        project_root,
        args.completion,
        "completion",
    )
    verified = verify_standalone_completion(
        completion_path,
        project_root=project_root,
        expected_purpose=args.purpose,
    )
    _emit({
        "ok": True,
        "purpose": verified.purpose,
        "invocationId": verified.invocation_id,
        "metadataPath": str(verified.prompt_metadata_path),
        "resultPath": str(verified.result_path),
        "completionPath": str(verified.completion_path),
        "resultEnvelopeDigest": verified.result_envelope_digest,
        "returnedBody": verified.returned_body,
    }, args.json)
