"""市場情報 MCP fetch candidate queue writer review handoff。

本模組只審核 post-closeout inventory review 之後的 operator handoff 摘要；
API/UI 不讀 approval token、不執行 CLI、不開 DB、不寫 queue、不寫檔、
不做 inventory query、不掛 scheduler。
"""

from services.market_intel.mcp_fetch_candidate_queue_writer_preflight import TARGET_TABLE
from services.market_intel.mcp_fetch_candidate_queue_writer_run_readiness import (
    ARTIFACT_PREFIX,
)
from services.market_intel.mcp_fetch_candidate_queue_writer_post_closeout_inventory_review import (
    FORBIDDEN_SECRET_KEYS,
    SAFE_SECRET_METADATA_KEYS,
    _as_dict,
    _as_list,
    _blocked_side_effects,
    _contains_forbidden_key,
    _safe_int,
    _safe_path,
    _safe_text,
    build_mcp_fetch_candidate_queue_writer_post_closeout_inventory_review_preview,
)
from services.market_intel.mcp_fetch_candidate_queue_writer_review_handoff_sample import (
    build_sample_writer_review_handoff_package,
)

_HANDOFF_BLOCKED_SIDE_EFFECT_KEYS = (
    "allow_api_candidate_review_update",
    "allow_api_execution",
    "allow_api_file_write",
    "allow_api_queue_write",
    "allow_cli_execution",
    "allow_database_write",
    "api_candidate_review_update_executed",
    "api_created_review_handoff_file",
    "api_created_review_queue",
    "api_executed_candidate_review",
    "api_inventory_query_executed",
    "api_review_handoff_persisted",
    "api_review_state_update_executed",
    "api_updates_review_state",
    "api_writes_review_queue",
    "candidate_queue_review_created_by_api",
    "candidate_queue_review_handoff_file_written",
    "candidate_queue_review_handoff_persisted",
    "candidate_queue_review_started_by_api",
    "candidate_review_file_written",
    "candidate_review_queue_written",
    "handoff_file_written",
    "handoff_persisted",
    "manual_review_record_written",
    "queue_review_persisted",
    "review_queue_file_written",
    "review_queue_row_written",
    "review_state_update_executed",
)


def _blocked_handoff_side_effects(payload):
    found = list(_blocked_side_effects(payload))

    def visit(value, path):
        if isinstance(value, dict):
            for key, item in value.items():
                normalized_key = str(key).lower()
                key_path = f"{path}.{key}" if path else key
                if normalized_key in _HANDOFF_BLOCKED_SIDE_EFFECT_KEYS and bool(item):
                    found.append(key_path)
                visit(item, key_path)
        elif isinstance(value, list):
            for index, item in enumerate(value):
                visit(item, f"{path}[{index}]")

    visit(payload, "")
    return sorted(set(found))


def _inventory_review_from_inputs(inventory_package, inventory_result, phase):
    if isinstance(inventory_result, dict) and inventory_result:
        return inventory_result

    inventory_package = _as_dict(inventory_package)
    return build_mcp_fetch_candidate_queue_writer_post_closeout_inventory_review_preview(
        writer_run_closeout_review_package=(
            inventory_package.get("writer_run_closeout_review_package")
            or inventory_package.get("candidate_queue_writer_run_closeout_review")
            or inventory_package.get("writer_run_closeout_review")
            or inventory_package.get("run_closeout_review_package")
            or {}
        ),
        writer_run_closeout_review_result=(
            inventory_package.get("writer_run_closeout_review_result")
            or inventory_package.get(
                "mcp_fetch_candidate_queue_writer_run_closeout_review"
            )
        ),
        operator_inventory=(
            inventory_package.get("operator_inventory")
            or inventory_package.get("post_closeout_inventory")
            or inventory_package.get("writer_post_closeout_inventory")
            or inventory_package.get("inventory_payload")
            or inventory_package.get("inventory")
        ),
        phase=phase,
    )


def _inventory_review_summary(inventory_result):
    inventory_result = _as_dict(inventory_result)
    operator_inventory = _as_dict(inventory_result.get("operator_inventory_summary"))
    side_effects_clear = bool(
        not inventory_result.get("payload_persisted")
        and not inventory_result.get("post_closeout_inventory_review_persisted")
        and not inventory_result.get("post_closeout_inventory_persisted")
        and not inventory_result.get("post_closeout_inventory_file_written")
        and not inventory_result.get("inventory_file_written")
        and not inventory_result.get("package_artifact_created")
        and not inventory_result.get("network_request_allowed")
        and not inventory_result.get("api_executes_cli")
        and not inventory_result.get("api_reads_approval_token")
        and not inventory_result.get("api_opens_database_connection")
        and not inventory_result.get("api_writes_database")
        and not inventory_result.get("api_writes_file")
        and not inventory_result.get("api_uses_external_network")
        and not inventory_result.get("api_inventory_query_executed")
        and not inventory_result.get("post_closeout_query_executed_by_api")
        and not inventory_result.get("database_write_executed")
        and not inventory_result.get("cli_executed")
        and not inventory_result.get("file_written")
        and not inventory_result.get("scheduler_attached")
        and not inventory_result.get("candidate_queue_created")
        and not inventory_result.get("candidate_queue_persisted")
        and not inventory_result.get("candidate_review_state_updated")
    )
    return {
        "mode": inventory_result.get("mode"),
        "accepted": bool(
            inventory_result.get(
                "mcp_fetch_candidate_queue_writer_post_closeout_inventory_review_accepted"
            )
        ),
        "ready_for_candidate_queue_review_handoff": bool(
            inventory_result.get("ready_for_candidate_queue_review_handoff")
        ),
        "inventory_review_id": _safe_text(
            operator_inventory.get("inventory_review_id"), 160
        ),
        "closeout_id": _safe_text(operator_inventory.get("closeout_id"), 160),
        "run_package_id": _safe_text(
            operator_inventory.get("run_package_id"), 160
        ),
        "receipt_id": _safe_text(operator_inventory.get("receipt_id"), 160),
        "target_table": _safe_text(operator_inventory.get("target_table"), 160),
        "payload_row_count": _safe_int(operator_inventory.get("payload_row_count")),
        "found_row_count": _safe_int(operator_inventory.get("found_row_count")),
        "inventory_artifact_path": _safe_text(
            operator_inventory.get("inventory_artifact_path")
        ),
        "closeout_review_artifact_path": _safe_text(
            operator_inventory.get("closeout_review_artifact_path")
        ),
        "read_only_query_result_path": _safe_text(
            operator_inventory.get("read_only_query_result_path")
        ),
        "inventory_artifact_path_safe": bool(
            operator_inventory.get("inventory_artifact_path_safe")
        ),
        "closeout_review_artifact_path_safe": bool(
            operator_inventory.get("closeout_review_artifact_path_safe")
        ),
        "read_only_query_result_path_safe": bool(
            operator_inventory.get("read_only_query_result_path_safe")
        ),
        "side_effects_clear": side_effects_clear,
        "blocked_reasons": inventory_result.get("blocked_reasons", []),
    }


def _operator_handoff_summary(operator_handoff):
    operator_handoff = _as_dict(operator_handoff)
    confirmations = _as_dict(operator_handoff.get("operator_confirmations"))
    contract = _as_dict(operator_handoff.get("review_contract"))
    required_columns = [
        _safe_text(item, 120)
        for item in _as_list(contract.get("required_columns"))
        if _safe_text(item, 120)
    ]
    allowed_manual_actions = [
        _safe_text(item, 120)
        for item in _as_list(contract.get("allowed_manual_actions"))
        if _safe_text(item, 120)
    ]
    forbidden_api_actions = [
        _safe_text(item, 120)
        for item in _as_list(contract.get("forbidden_api_actions"))
        if _safe_text(item, 120)
    ]
    return {
        "provided_keys": sorted(operator_handoff.keys()),
        "handoff_id": _safe_text(operator_handoff.get("handoff_id"), 160),
        "inventory_review_id": _safe_text(
            operator_handoff.get("inventory_review_id"), 160
        ),
        "closeout_id": _safe_text(operator_handoff.get("closeout_id"), 160),
        "run_package_id": _safe_text(operator_handoff.get("run_package_id"), 160),
        "receipt_id": _safe_text(operator_handoff.get("receipt_id"), 160),
        "target_table": _safe_text(operator_handoff.get("target_table"), 160),
        "payload_row_count": _safe_int(operator_handoff.get("payload_row_count")),
        "expected_payload_row_count": _safe_int(
            operator_handoff.get("expected_payload_row_count")
            or operator_handoff.get("payload_row_count")
        ),
        "expected_review_state": _safe_text(
            operator_handoff.get("expected_review_state")
            or contract.get("expected_review_state"),
            80,
        ),
        "contract_expected_review_state": _safe_text(
            contract.get("expected_review_state"), 80
        ),
        "required_columns": required_columns,
        "allowed_manual_actions": allowed_manual_actions,
        "forbidden_api_actions": forbidden_api_actions,
        "review_scope": _safe_text(operator_handoff.get("review_scope"), 120),
        "next_queue": _safe_text(operator_handoff.get("next_queue"), 120),
        "review_queue_artifact_path": _safe_text(
            operator_handoff.get("review_queue_artifact_path")
        ),
        "inventory_artifact_path": _safe_text(
            operator_handoff.get("inventory_artifact_path")
        ),
        "handoff_artifact_path": _safe_text(
            operator_handoff.get("handoff_artifact_path")
        ),
        "review_queue_artifact_path_safe": _safe_path(
            operator_handoff.get("review_queue_artifact_path"),
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "inventory_artifact_path_safe": _safe_path(
            operator_handoff.get("inventory_artifact_path"),
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "handoff_artifact_path_safe": _safe_path(
            operator_handoff.get("handoff_artifact_path"),
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "inventory_reviewed": bool(confirmations.get("inventory_reviewed")),
        "inventory_artifacts_preserved": bool(
            confirmations.get("inventory_artifacts_preserved")
        ),
        "candidate_queue_review_only": bool(
            confirmations.get("candidate_queue_review_only")
        ),
        "manual_review_only": bool(
            confirmations.get("manual_review_only")
            or confirmations.get("candidate_queue_review_only")
        ),
        "manual_review_required": bool(confirmations.get("manual_review_required")),
        "no_approval_token_payload": bool(
            confirmations.get("no_approval_token_payload")
        ),
        "no_api_cli_execution": bool(confirmations.get("no_api_cli_execution")),
        "no_api_database_write": bool(confirmations.get("no_api_database_write")),
        "no_api_inventory_query": bool(
            confirmations.get("no_api_inventory_query")
        ),
        "no_api_file_write": bool(confirmations.get("no_api_file_write")),
        "no_scheduler_attach": bool(confirmations.get("no_scheduler_attach")),
        "no_api_review_state_update": bool(
            confirmations.get("no_api_review_state_update")
        ),
        "no_api_queue_insert": bool(confirmations.get("no_api_queue_insert")),
        "api_execution_allowed": bool(operator_handoff.get("api_execution_allowed")),
        "real_write_allowed_by_api": bool(
            operator_handoff.get("real_write_allowed_by_api")
        ),
        "api_candidate_review_allowed": bool(
            operator_handoff.get("api_candidate_review_allowed")
        ),
        "api_updates_review_state": bool(operator_handoff.get("api_updates_review_state")),
        "secret_or_token_submitted_to_api": _contains_forbidden_key(
            operator_handoff,
            FORBIDDEN_SECRET_KEYS,
            safe_keys=SAFE_SECRET_METADATA_KEYS,
        ),
        "blocked_side_effects": _blocked_handoff_side_effects(operator_handoff),
    }


def _handoff_gates(*, inventory_received, handoff_received, inventory, handoff):
    operator_confirmed_boundaries = bool(
        handoff["inventory_reviewed"]
        and handoff["inventory_artifacts_preserved"]
        and handoff["candidate_queue_review_only"]
        and handoff["manual_review_only"]
        and handoff["manual_review_required"]
        and handoff["no_approval_token_payload"]
        and handoff["no_api_cli_execution"]
        and handoff["no_api_database_write"]
        and handoff["no_api_inventory_query"]
        and handoff["no_api_file_write"]
        and handoff["no_api_review_state_update"]
        and handoff["no_api_queue_insert"]
        and handoff["no_scheduler_attach"]
    )
    contract_valid = bool(
        handoff["expected_review_state"] == "needs_review"
        and handoff["contract_expected_review_state"] == "needs_review"
        and {"dedupe_key", "review_state"}.issubset(set(handoff["required_columns"]))
        and handoff["allowed_manual_actions"]
    )
    forbids_api_mutations = bool(
        {"update_review_state", "insert_missing_queue_row"}.issubset(
            set(handoff["forbidden_api_actions"])
        )
        and handoff["no_api_review_state_update"]
        and handoff["no_api_queue_insert"]
    )
    return [
        {
            "key": "post_closeout_inventory_review_payload_or_result_received",
            "label": "已提供 post-closeout inventory review package 或已審核結果",
            "passed": inventory_received,
        },
        {
            "key": "post_closeout_inventory_review_accepted",
            "label": "post-closeout inventory review gate 必須已通過",
            "passed": inventory["accepted"],
        },
        {
            "key": "post_closeout_inventory_ready_for_handoff",
            "label": "inventory review 必須只放行到 candidate queue review handoff",
            "passed": inventory["ready_for_candidate_queue_review_handoff"],
        },
        {
            "key": "post_closeout_inventory_side_effect_free",
            "label": "inventory review 未顯示 API 執行、寫 DB、寫檔、查 DB 或掛 scheduler",
            "passed": inventory["side_effects_clear"],
        },
        {
            "key": "post_closeout_inventory_identity_recorded",
            "label": "inventory review 必須保留 inventory_review_id、closeout_id、run_package_id 與 receipt_id",
            "passed": bool(
                inventory["inventory_review_id"]
                and inventory["closeout_id"]
                and inventory["run_package_id"]
                and inventory["receipt_id"]
            ),
        },
        {
            "key": "candidate_queue_review_handoff_payload_received",
            "label": "已提供 operator candidate queue review handoff 摘要",
            "passed": handoff_received,
        },
        {
            "key": "candidate_queue_review_handoff_identity_recorded",
            "label": "handoff 必須記錄 handoff_id、inventory_review_id、closeout_id、run_package_id 與 receipt_id",
            "passed": bool(
                handoff["handoff_id"]
                and handoff["inventory_review_id"]
                and handoff["closeout_id"]
                and handoff["run_package_id"]
                and handoff["receipt_id"]
            ),
        },
        {
            "key": "candidate_queue_review_handoff_identity_matches_inventory",
            "label": "handoff identity 必須對齊 inventory review",
            "passed": bool(
                handoff["inventory_review_id"] == inventory["inventory_review_id"]
                and handoff["closeout_id"] == inventory["closeout_id"]
                and handoff["run_package_id"] == inventory["run_package_id"]
                and handoff["receipt_id"] == inventory["receipt_id"]
            ),
        },
        {
            "key": "candidate_queue_review_handoff_target_table_safe",
            "label": "target table 必須是 market_alert_review_queue",
            "passed": handoff["target_table"] == TARGET_TABLE,
        },
        {
            "key": "candidate_queue_review_handoff_row_count_matches_inventory",
            "label": "handoff row count 必須對齊 inventory payload rows",
            "passed": bool(
                handoff["expected_payload_row_count"]
                and handoff["expected_payload_row_count"]
                == inventory["payload_row_count"]
                and handoff["payload_row_count"] == inventory["payload_row_count"]
                and inventory["found_row_count"] == inventory["payload_row_count"]
            ),
        },
        {
            "key": "candidate_queue_review_handoff_artifact_paths_safe",
            "label": "review queue、inventory 與 handoff artifact paths 必須安全",
            "passed": bool(
                handoff["review_queue_artifact_path_safe"]
                and handoff["inventory_artifact_path_safe"]
                and handoff["handoff_artifact_path_safe"]
                and handoff["inventory_artifact_path"]
                == inventory["inventory_artifact_path"]
            ),
        },
        {
            "key": "candidate_queue_review_handoff_scope_safe",
            "label": "handoff 僅能交接到 manual candidate queue review",
            "passed": bool(
                handoff["review_scope"] == "candidate_queue_review"
                and handoff["next_queue"] == "manual_candidate_queue_review"
            ),
        },
        {
            "key": "candidate_queue_review_handoff_contract_valid",
            "label": "review contract 必須鎖定 needs_review、必備欄位與人工操作",
            "passed": contract_valid,
        },
        {
            "key": "candidate_queue_review_handoff_forbids_api_mutations",
            "label": "review contract 必須禁止 API 更新 review_state 或補寫 queue row",
            "passed": forbids_api_mutations,
        },
        {
            "key": "candidate_queue_review_handoff_operator_boundaries_confirmed",
            "label": "操作員確認僅進人工 review，且 API 未執行 CLI/DB/query/file/scheduler",
            "passed": operator_confirmed_boundaries,
        },
        {
            "key": "candidate_queue_review_handoff_no_api_execution_or_real_write",
            "label": "handoff payload 不得允許 API execution、real write 或 API review update",
            "passed": bool(
                not handoff["api_execution_allowed"]
                and not handoff["real_write_allowed_by_api"]
                and not handoff["api_candidate_review_allowed"]
                and not handoff["api_updates_review_state"]
            ),
        },
        {
            "key": "candidate_queue_review_handoff_no_secret_or_token_key",
            "label": "handoff payload 不得包含 secret、cookie、password 或 token key",
            "passed": not handoff["secret_or_token_submitted_to_api"],
        },
        {
            "key": "candidate_queue_review_handoff_side_effect_free",
            "label": "handoff payload 不得要求 API 寫檔、執行、查 DB、寫 DB 或掛 scheduler",
            "passed": not handoff["blocked_side_effects"],
        },
    ]


def build_mcp_fetch_candidate_queue_writer_review_handoff_preview(
    *,
    writer_post_closeout_inventory_review_package=None,
    writer_post_closeout_inventory_review_result=None,
    operator_handoff=None,
    phase=None,
):
    """建立 writer review handoff；不執行查詢、寫檔或寫入。"""
    writer_post_closeout_inventory_review_package = _as_dict(
        writer_post_closeout_inventory_review_package
    )
    inventory_result_received = bool(
        isinstance(writer_post_closeout_inventory_review_result, dict)
        and writer_post_closeout_inventory_review_result
    )
    handoff_valid_object = (
        isinstance(operator_handoff, dict) if operator_handoff is not None else True
    )
    handoff_payload = _as_dict(operator_handoff)
    inventory_received = bool(
        writer_post_closeout_inventory_review_package or inventory_result_received
    )
    inventory_result = (
        _inventory_review_from_inputs(
            writer_post_closeout_inventory_review_package,
            writer_post_closeout_inventory_review_result,
            phase,
        )
        if inventory_received
        else {}
    )
    payload_received = bool(
        inventory_received or handoff_payload or operator_handoff is not None
    )
    handoff_received = bool(handoff_payload)
    inventory = _inventory_review_summary(inventory_result)
    handoff = _operator_handoff_summary(handoff_payload)
    gates = _handoff_gates(
        inventory_received=inventory_received,
        handoff_received=handoff_received and handoff_valid_object,
        inventory=inventory,
        handoff=handoff,
    )
    blocked_reasons = [gate["key"] for gate in gates if not gate["passed"]]
    if not handoff_valid_object:
        blocked_reasons.append("operator_handoff_payload_valid_object")
    accepted = bool(payload_received and not blocked_reasons)

    return {
        "mode": (
            "mcp_fetch_candidate_queue_writer_review_handoff"
            if payload_received
            else "mcp_fetch_candidate_queue_writer_review_handoff_preview"
        ),
        "phase": phase,
        "writer_review_handoff_payload_received": payload_received,
        "review_handoff_payload_received": payload_received,
        "writer_post_closeout_inventory_review_received": inventory_received,
        "operator_handoff_received": handoff_received,
        "operator_handoff_valid_object": handoff_valid_object,
        "writer_post_closeout_inventory_review_accepted": inventory["accepted"],
        "mcp_fetch_candidate_queue_writer_review_handoff_accepted": accepted,
        "candidate_queue_writer_review_handoff_ready": accepted,
        "ready_for_candidate_queue_review_inventory": accepted,
        "ready_for_candidate_queue_human_review": accepted,
        "ready_for_candidate_queue_review_decision": False,
        "ready_for_api_database_write": False,
        "ready_for_real_write": False,
        "ready_for_scheduler_attach": False,
        "ready_for_candidate_queue_review_api_update": False,
        "ready_for_api_review_state_update": False,
        "network_request_allowed": False,
        "api_executes_cli": False,
        "api_reads_approval_token": False,
        "api_opens_database_connection": False,
        "api_writes_database": False,
        "api_writes_file": False,
        "api_uses_external_network": False,
        "api_inventory_query_executed": False,
        "api_candidate_review_update_executed": False,
        "api_updates_review_state": False,
        "post_closeout_query_executed_by_api": False,
        "payload_row_count": handoff["payload_row_count"],
        "gate_count": len(gates),
        "passed_gate_count": sum(1 for gate in gates if gate["passed"]),
        "blocked_reasons": blocked_reasons,
        "gates": gates,
        "writer_post_closeout_inventory_review_summary": inventory,
        "operator_handoff_summary": handoff,
        "sample_writer_review_handoff_package": (
            build_sample_writer_review_handoff_package()
        ),
        "next_operator_steps": [
            "Handoff 通過後，只代表可交給人工 candidate queue review inventory",
            "API/UI 仍不得自動寫 review state、補寫 queue row、讀 token 或掛 scheduler",
            "後續 review 必須引用已審核 artifact，並保留人工決策紀錄與 false-positive 保護",
        ],
        "payload_persisted": False,
        "review_handoff_persisted": False,
        "writer_review_handoff_persisted": False,
        "candidate_queue_review_handoff_persisted": False,
        "candidate_queue_review_handoff_file_written": False,
        "handoff_file_written": False,
        "review_queue_file_written": False,
        "candidate_review_file_written": False,
        "package_artifact_created": False,
        "database_session_created": False,
        "database_commit_executed": False,
        "database_write_executed": False,
        "external_network_executed": False,
        "cli_executed": False,
        "file_written": False,
        "writes_executed": False,
        "would_write_database": False,
        "scheduler_attached": False,
        "candidate_queue_created": False,
        "candidate_queue_persisted": False,
        "candidate_review_state_updated": False,
    }
