"""市場情報 MCP review decision approval writer preflight。

本模組只審核人工 approval 後的 writer preflight 摘要；
API/UI 不讀 approval token、不執行 CLI、不開 DB、不寫 preflight record、
不更新 review_state、不寫 decision/match、不補 queue、不掛 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,
)
from services.market_intel.mcp_fetch_candidate_queue_writer_review_decision_approval import (
    build_mcp_fetch_candidate_queue_writer_review_decision_approval_preview,
)
from services.market_intel.mcp_fetch_candidate_queue_writer_review_decision_approval_writer_preflight_gates import (
    ALLOWED_PREFLIGHT_MODES,
    ALLOWED_PREFLIGHT_SCOPES,
    TARGET_OPERATION,
    build_approval_writer_preflight_gates,
)
from services.market_intel.mcp_fetch_candidate_queue_writer_review_decision_approval_writer_preflight_sample import (
    build_sample_writer_review_decision_approval_writer_preflight_package,
)
from services.market_intel.mcp_fetch_candidate_queue_writer_review_decision_gates import (
    ALLOWED_REVIEW_DECISIONS,
)


_PREFLIGHT_BLOCKED_SIDE_EFFECT_KEYS = (
    "allow_api_candidate_review_update",
    "allow_api_execution",
    "allow_api_file_write",
    "allow_api_match_write",
    "allow_api_queue_insert",
    "allow_api_queue_write",
    "allow_cli_execution",
    "allow_database_write",
    "api_candidate_review_update_executed",
    "api_created_writer_preflight_file",
    "api_executes_review_state_writer",
    "api_review_state_update_executed",
    "api_updates_review_state",
    "api_write_allowed",
    "api_writes_approval_record",
    "api_writes_decision_record",
    "api_writes_match_result",
    "api_writes_preflight_record",
    "candidate_queue_review_decision_approval_writer_preflight_file_written",
    "candidate_queue_review_decision_approval_writer_preflight_persisted",
    "decision_record_written",
    "market_product_match_written",
    "match_result_written",
    "preflight_record_written",
    "review_state_update_executed",
    "writer_preflight_file_written",
    "writer_preflight_persisted",
)


def _blocked_preflight_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 _PREFLIGHT_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 _approval_from_inputs(approval_package, approval_result, phase):
    if isinstance(approval_result, dict) and approval_result:
        return approval_result

    approval_package = _as_dict(approval_package)
    return build_mcp_fetch_candidate_queue_writer_review_decision_approval_preview(
        writer_review_decision_package=(
            approval_package.get("writer_review_decision_package")
            or approval_package.get("candidate_queue_writer_review_decision")
            or approval_package.get("writer_review_decision")
            or approval_package.get("review_decision_package")
            or approval_package.get("review_decision")
            or {}
        ),
        writer_review_decision_result=(
            approval_package.get("writer_review_decision_result")
            or approval_package.get(
                "mcp_fetch_candidate_queue_writer_review_decision"
            )
        ),
        operator_review_decision_approval=(
            approval_package.get("operator_review_decision_approval")
            or approval_package.get("operator_review_approval")
            or approval_package.get("candidate_queue_review_decision_approval")
            or approval_package.get("writer_review_decision_approval")
            or approval_package.get("approval_payload")
            or approval_package.get("approval")
        ),
        phase=phase,
    )


def _approval_summary(approval_result):
    approval_result = _as_dict(approval_result)
    approval = _as_dict(
        approval_result.get("operator_review_decision_approval_summary")
        or approval_result.get("operator_review_approval_summary")
    )
    side_effects_clear = bool(
        not approval_result.get("payload_persisted")
        and not approval_result.get("review_decision_approval_persisted")
        and not approval_result.get("candidate_queue_review_decision_approval_persisted")
        and not approval_result.get("candidate_queue_review_decision_approval_file_written")
        and not approval_result.get("approval_record_written")
        and not approval_result.get("decision_record_written")
        and not approval_result.get("match_result_written")
        and not approval_result.get("api_executes_cli")
        and not approval_result.get("api_reads_approval_token")
        and not approval_result.get("api_opens_database_connection")
        and not approval_result.get("api_writes_database")
        and not approval_result.get("api_writes_file")
        and not approval_result.get("api_updates_review_state")
        and not approval_result.get("api_writes_approval_record")
        and not approval_result.get("api_writes_decision_record")
        and not approval_result.get("api_writes_match_result")
        and not approval_result.get("database_write_executed")
        and not approval_result.get("database_commit_executed")
        and not approval_result.get("cli_executed")
        and not approval_result.get("file_written")
        and not approval_result.get("scheduler_attached")
        and not approval_result.get("candidate_queue_persisted")
        and not approval_result.get("candidate_review_state_updated")
    )
    return {
        "mode": approval_result.get("mode"),
        "accepted": bool(
            approval_result.get(
                "mcp_fetch_candidate_queue_writer_review_decision_approval_accepted"
            )
        ),
        "ready_for_candidate_queue_review_decision_writer_preflight": bool(
            approval_result.get(
                "ready_for_candidate_queue_review_decision_writer_preflight"
            )
        ),
        "approval_id": _safe_text(approval.get("approval_id"), 160),
        "review_decision_id": _safe_text(approval.get("review_decision_id"), 160),
        "review_inventory_id": _safe_text(approval.get("review_inventory_id"), 160),
        "handoff_id": _safe_text(approval.get("handoff_id"), 160),
        "inventory_review_id": _safe_text(approval.get("inventory_review_id"), 160),
        "closeout_id": _safe_text(approval.get("closeout_id"), 160),
        "run_package_id": _safe_text(approval.get("run_package_id"), 160),
        "receipt_id": _safe_text(approval.get("receipt_id"), 160),
        "target_table": _safe_text(approval.get("target_table"), 160),
        "approved_row_count": _safe_int(
            approval.get("approved_row_count") or approval.get("approval_row_count")
        ),
        "expected_dedupe_keys": [
            _safe_text(item, 160)
            for item in _as_list(approval.get("expected_dedupe_keys"))
            if _safe_text(item, 160)
        ],
        "approval_rows": _as_list(
            approval.get("approved_rows") or approval.get("approval_rows")
        ),
        "review_queue_artifact_path": _safe_text(
            approval.get("review_queue_artifact_path")
        ),
        "review_inventory_artifact_path": _safe_text(
            approval.get("review_inventory_artifact_path")
        ),
        "review_decision_artifact_path": _safe_text(
            approval.get("review_decision_artifact_path")
        ),
        "approval_artifact_path": _safe_text(
            approval.get("approval_artifact_path")
            or approval.get("review_decision_approval_artifact_path")
        ),
        "side_effects_clear": side_effects_clear,
        "blocked_reasons": approval_result.get("blocked_reasons", []),
    }


def _preflight_row_summary(row):
    row = _as_dict(row)
    decision_evidence_ref = _safe_text(row.get("decision_evidence_ref"))
    approval_evidence_ref = _safe_text(row.get("approval_evidence_ref"))
    preflight_evidence_ref = _safe_text(
        row.get("preflight_evidence_ref") or row.get("evidence_ref")
    )
    return {
        "dedupe_key": _safe_text(row.get("dedupe_key"), 160),
        "current_review_state": _safe_text(row.get("current_review_state"), 80),
        "approved_review_decision": _safe_text(
            row.get("approved_review_decision"), 80
        ),
        "target_review_state": _safe_text(row.get("target_review_state"), 80),
        "statement_type": _safe_text(row.get("statement_type"), 80),
        "operation": _safe_text(row.get("operation"), 80),
        "writer_action": _safe_text(row.get("writer_action"), 120),
        "evidence_lane": _safe_text(row.get("evidence_lane"), 120),
        "decision_evidence_ref": decision_evidence_ref,
        "approval_evidence_ref": approval_evidence_ref,
        "preflight_evidence_ref": preflight_evidence_ref,
        "decision_evidence_ref_safe": _safe_path(
            decision_evidence_ref,
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "approval_evidence_ref_safe": _safe_path(
            approval_evidence_ref,
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "preflight_evidence_ref_safe": _safe_path(
            preflight_evidence_ref,
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "preflight_notes_present": bool(_safe_text(row.get("preflight_notes"))),
        "hard_veto_present": bool(row.get("hard_veto_present")),
        "stronger_existing_match_conflict": bool(
            row.get("stronger_existing_match_conflict")
        ),
        "false_positive_guard_passed": bool(row.get("false_positive_guard_passed")),
        "variant_conflict_checked": bool(row.get("variant_conflict_checked")),
        "overwrite_protection_checked": bool(row.get("overwrite_protection_checked")),
        "exact_identity_confirmed": bool(row.get("exact_identity_confirmed")),
    }


def _operator_preflight_summary(operator_preflight):
    operator_preflight = _as_dict(operator_preflight)
    confirmations = _as_dict(operator_preflight.get("operator_confirmations"))
    rows = [
        _preflight_row_summary(row)
        for row in _as_list(operator_preflight.get("preflight_rows"))
    ]
    expected_dedupe_keys = [
        _safe_text(item, 160)
        for item in _as_list(operator_preflight.get("expected_dedupe_keys"))
        if _safe_text(item, 160)
    ]
    return {
        "provided_keys": sorted(operator_preflight.keys()),
        "writer_preflight_id": _safe_text(
            operator_preflight.get("writer_preflight_id")
            or operator_preflight.get("preflight_id"),
            160,
        ),
        "approval_id": _safe_text(operator_preflight.get("approval_id"), 160),
        "review_decision_id": _safe_text(
            operator_preflight.get("review_decision_id"), 160
        ),
        "review_inventory_id": _safe_text(
            operator_preflight.get("review_inventory_id"), 160
        ),
        "handoff_id": _safe_text(operator_preflight.get("handoff_id"), 160),
        "inventory_review_id": _safe_text(
            operator_preflight.get("inventory_review_id"), 160
        ),
        "closeout_id": _safe_text(operator_preflight.get("closeout_id"), 160),
        "run_package_id": _safe_text(operator_preflight.get("run_package_id"), 160),
        "receipt_id": _safe_text(operator_preflight.get("receipt_id"), 160),
        "target_table": _safe_text(operator_preflight.get("target_table"), 160),
        "target_operation": _safe_text(
            operator_preflight.get("target_operation"), 160
        ),
        "preflight_scope": _safe_text(
            operator_preflight.get("preflight_scope"), 160
        ),
        "preflight_mode": _safe_text(
            operator_preflight.get("preflight_mode"), 160
        ),
        "expected_current_review_state": _safe_text(
            operator_preflight.get("expected_current_review_state"), 80
        ),
        "preflight_row_count": _safe_int(
            operator_preflight.get("preflight_row_count")
        ),
        "expected_preflight_row_count": _safe_int(
            operator_preflight.get("expected_preflight_row_count")
            or operator_preflight.get("preflight_row_count")
        ),
        "expected_dedupe_keys": expected_dedupe_keys,
        "preflight_rows": rows,
        "review_queue_artifact_path": _safe_text(
            operator_preflight.get("review_queue_artifact_path")
        ),
        "review_inventory_artifact_path": _safe_text(
            operator_preflight.get("review_inventory_artifact_path")
        ),
        "review_decision_artifact_path": _safe_text(
            operator_preflight.get("review_decision_artifact_path")
        ),
        "approval_artifact_path": _safe_text(
            operator_preflight.get("approval_artifact_path")
            or operator_preflight.get("review_decision_approval_artifact_path")
        ),
        "writer_preflight_artifact_path": _safe_text(
            operator_preflight.get("writer_preflight_artifact_path")
            or operator_preflight.get("preflight_artifact_path")
        ),
        "review_queue_artifact_path_safe": _safe_path(
            operator_preflight.get("review_queue_artifact_path"),
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "review_inventory_artifact_path_safe": _safe_path(
            operator_preflight.get("review_inventory_artifact_path"),
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "review_decision_artifact_path_safe": _safe_path(
            operator_preflight.get("review_decision_artifact_path"),
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "approval_artifact_path_safe": _safe_path(
            operator_preflight.get("approval_artifact_path")
            or operator_preflight.get("review_decision_approval_artifact_path"),
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "writer_preflight_artifact_path_safe": _safe_path(
            operator_preflight.get("writer_preflight_artifact_path")
            or operator_preflight.get("preflight_artifact_path"),
            prefixes=(ARTIFACT_PREFIX,),
            suffixes=(".json",),
        ),
        "approval_checked": bool(confirmations.get("approval_checked")),
        "writer_preflight_is_cli_only": bool(
            confirmations.get("writer_preflight_is_cli_only")
        ),
        "manual_record_required": bool(confirmations.get("manual_record_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_review_state_update": bool(
            confirmations.get("no_api_review_state_update")
        ),
        "no_api_decision_record_write": bool(
            confirmations.get("no_api_decision_record_write")
        ),
        "no_api_match_write": bool(confirmations.get("no_api_match_write")),
        "no_api_queue_insert": bool(confirmations.get("no_api_queue_insert")),
        "no_api_file_write": bool(confirmations.get("no_api_file_write")),
        "no_scheduler_attach": bool(confirmations.get("no_scheduler_attach")),
        "rows_match_approval": bool(confirmations.get("rows_match_approval")),
        "schema_preflight_required": bool(
            confirmations.get("schema_preflight_required")
        ),
        "status_transition_reviewed": bool(
            confirmations.get("status_transition_reviewed")
        ),
        "stronger_existing_match_guard_preserved": bool(
            confirmations.get("stronger_existing_match_guard_preserved")
        ),
        "false_positive_guard_completed": bool(
            confirmations.get("false_positive_guard_completed")
        ),
        "variant_sensitive_review_completed": bool(
            confirmations.get("variant_sensitive_review_completed")
        ),
        "hard_veto_respected": bool(confirmations.get("hard_veto_respected")),
        "api_execution_allowed": bool(operator_preflight.get("api_execution_allowed")),
        "real_write_allowed_by_api": bool(
            operator_preflight.get("real_write_allowed_by_api")
        ),
        "api_candidate_review_allowed": bool(
            operator_preflight.get("api_candidate_review_allowed")
        ),
        "api_updates_review_state": bool(
            operator_preflight.get("api_updates_review_state")
        ),
        "api_writes_preflight_record": bool(
            operator_preflight.get("api_writes_preflight_record")
        ),
        "api_writes_approval_record": bool(
            operator_preflight.get("api_writes_approval_record")
        ),
        "api_writes_decision_record": bool(
            operator_preflight.get("api_writes_decision_record")
        ),
        "api_writes_match_result": bool(
            operator_preflight.get("api_writes_match_result")
        ),
        "secret_or_token_submitted_to_api": _contains_forbidden_key(
            operator_preflight,
            FORBIDDEN_SECRET_KEYS,
            safe_keys=SAFE_SECRET_METADATA_KEYS,
        ),
        "blocked_side_effects": _blocked_preflight_side_effects(operator_preflight),
    }


def build_mcp_fetch_candidate_queue_writer_review_decision_approval_writer_preflight_preview(
    *,
    writer_review_decision_approval_package=None,
    writer_review_decision_approval_result=None,
    operator_review_decision_approval_writer_preflight=None,
    operator_writer_preflight=None,
    phase=None,
):
    """建立 approval writer preflight；不執行查詢、寫檔或寫入。"""
    if operator_review_decision_approval_writer_preflight is None:
        operator_review_decision_approval_writer_preflight = operator_writer_preflight
    writer_review_decision_approval_package = _as_dict(
        writer_review_decision_approval_package
    )
    approval_result_received = bool(
        isinstance(writer_review_decision_approval_result, dict)
        and writer_review_decision_approval_result
    )
    preflight_valid_object = (
        isinstance(operator_review_decision_approval_writer_preflight, dict)
        if operator_review_decision_approval_writer_preflight is not None
        else True
    )
    preflight_payload = _as_dict(
        operator_review_decision_approval_writer_preflight
    )
    approval_received = bool(
        writer_review_decision_approval_package or approval_result_received
    )
    approval_result = (
        _approval_from_inputs(
            writer_review_decision_approval_package,
            writer_review_decision_approval_result,
            phase,
        )
        if approval_received
        else {}
    )
    payload_received = bool(
        approval_received
        or preflight_payload
        or operator_review_decision_approval_writer_preflight is not None
    )
    preflight_received = bool(preflight_payload)
    approval = _approval_summary(approval_result)
    preflight = _operator_preflight_summary(preflight_payload)
    gates = build_approval_writer_preflight_gates(
        approval_received=approval_received,
        preflight_received=preflight_received and preflight_valid_object,
        approval=approval,
        preflight=preflight,
    )
    blocked_reasons = [gate["key"] for gate in gates if not gate["passed"]]
    if not preflight_valid_object:
        blocked_reasons.append(
            "operator_review_decision_approval_writer_preflight_valid_object"
        )
    accepted = bool(payload_received and not blocked_reasons)

    return {
        "mode": (
            "mcp_fetch_candidate_queue_writer_review_decision_approval_writer_preflight"
            if payload_received
            else "mcp_fetch_candidate_queue_writer_review_decision_approval_writer_preflight_preview"
        ),
        "phase": phase,
        "writer_review_decision_approval_writer_preflight_payload_received": (
            payload_received
        ),
        "writer_review_decision_approval_received": approval_received,
        "operator_writer_preflight_received": preflight_received,
        "operator_writer_preflight_valid_object": preflight_valid_object,
        "writer_review_decision_approval_accepted": approval["accepted"],
        "mcp_fetch_candidate_queue_writer_review_decision_approval_writer_preflight_accepted": accepted,
        "candidate_queue_writer_review_decision_approval_writer_preflight_ready": accepted,
        "ready_for_candidate_queue_review_decision_writer_cli_review": accepted,
        "ready_for_candidate_queue_review_state_writer": False,
        "ready_for_api_review_state_update": False,
        "ready_for_api_database_write": False,
        "ready_for_api_decision_record_write": False,
        "ready_for_api_match_write": False,
        "ready_for_real_write": False,
        "ready_for_scheduler_attach": 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_updates_review_state": False,
        "api_writes_preflight_record": False,
        "api_writes_approval_record": False,
        "api_writes_decision_record": False,
        "api_writes_match_result": False,
        "payload_row_count": preflight["preflight_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_review_decision_approval_summary": approval,
        "operator_writer_preflight_summary": preflight,
        "preflight_rows": preflight["preflight_rows"],
        "writer_preflight_contract": {
            "target_table": TARGET_TABLE,
            "target_operation": TARGET_OPERATION,
            "expected_current_state": "needs_review",
            "allowed_next_states": list(ALLOWED_REVIEW_DECISIONS),
            "allowed_preflight_modes": list(ALLOWED_PREFLIGHT_MODES),
            "allowed_preflight_scopes": list(ALLOWED_PREFLIGHT_SCOPES),
            "manual_cli_writer_required": True,
            "next_gate": "candidate_queue_review_decision_writer_cli_review",
            "forbidden_api_actions": [
                "update_review_state",
                "write_preflight_record",
                "write_approval_record",
                "write_decision_record",
                "write_match_result",
                "insert_missing_queue_row",
                "attach_scheduler",
            ],
        },
        "sample_writer_review_decision_approval_writer_preflight_package": (
            build_sample_writer_review_decision_approval_writer_preflight_package()
        ),
        "next_operator_steps": [
            "Writer preflight 通過後，只代表可進入 CLI review / run package 設計",
            "API/UI 仍不得自動寫 review_state、decision record、approval record 或 match result",
            "正式寫入仍必須走 shell-only token、read-only preflight、receipt 與 post-write smoke",
        ],
        "payload_persisted": False,
        "writer_preflight_persisted": False,
        "candidate_queue_review_decision_approval_writer_preflight_persisted": False,
        "candidate_queue_review_decision_approval_writer_preflight_file_written": False,
        "preflight_record_written": False,
        "preflight_record_file_written": False,
        "approval_record_written": False,
        "decision_record_written": False,
        "match_result_written": False,
        "market_product_match_written": False,
        "package_artifact_created": False,
        "database_connection_opened": 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,
    }
