"""候選審核 queue review_state writer CLI gate。

本模組只建立正式 shell/CLI 更新 review_state 前的 gate 狀態；
不讀環境 token、不執行 CLI、不連 DB、不更新 review_state。
"""

import hmac


APPROVAL_ENV_VAR = "MARKET_INTEL_QUEUE_WRITE_APPROVAL"
MIN_APPROVAL_TOKEN_LENGTH = 16
TARGET_TABLE = "market_alert_review_queue"
ALLOWED_DECISIONS = ("confirmed", "rejected", "deferred")
FORBIDDEN_TOKEN_KEYWORDS = (
    "approval_token",
    "approval-token",
    "market_intel_queue_write_approval",
)
SAFE_TOKEN_METADATA_KEYS = {
    "approval_token_present",
    "approval_token_valid",
    "approval_token_secret_configured",
}


def _as_dict(value):
    return value if isinstance(value, dict) else {}


def _as_list(value):
    if value is None:
        return []
    if isinstance(value, (list, tuple, set)):
        return list(value)
    return [value]


def _contains_forbidden_token_key(value):
    if isinstance(value, dict):
        for key, nested in value.items():
            normalized_key = str(key).lower()
            if normalized_key in SAFE_TOKEN_METADATA_KEYS and isinstance(nested, bool):
                continue
            if normalized_key == "approval_env_var" and nested == APPROVAL_ENV_VAR:
                continue
            if any(token_key in normalized_key for token_key in FORBIDDEN_TOKEN_KEYWORDS):
                return True
            if _contains_forbidden_token_key(nested):
                return True
    elif isinstance(value, list):
        return any(_contains_forbidden_token_key(item) for item in value)
    return False


def _approval_token_valid(approval_token, approval_token_secret):
    if not approval_token or not approval_token_secret:
        return False
    if len(str(approval_token_secret)) < MIN_APPROVAL_TOKEN_LENGTH:
        return False
    return hmac.compare_digest(str(approval_token), str(approval_token_secret))


def _statement_payloads(transaction_preview):
    payloads = []
    invalid = []
    for statement in _as_list(_as_dict(transaction_preview).get("statements")):
        statement = _as_dict(statement)
        payload = _as_dict(statement.get("parameter_preview"))
        current_state = (
            payload.get("expected_current_review_state")
            or statement.get("expected_current_review_state")
        )
        next_state = payload.get("next_review_state") or statement.get(
            "next_review_state"
        )
        dedupe_key = payload.get("dedupe_key") or _as_dict(statement.get("lookup")).get(
            "dedupe_key"
        )
        normalized = {
            "idempotency_key": statement.get("idempotency_key"),
            "dedupe_key": dedupe_key,
            "expected_current_review_state": current_state,
            "next_review_state": next_state,
            "statement_type": statement.get("statement_type"),
            "operation": statement.get("operation"),
            "parameter_payload_hash": statement.get("parameter_payload_hash"),
        }
        missing_fields = [
            key
            for key in (
                "dedupe_key",
                "expected_current_review_state",
                "next_review_state",
            )
            if not normalized.get(key)
        ]
        transition_invalid = bool(
            current_state != "needs_review" or next_state not in ALLOWED_DECISIONS
        )
        operation_invalid = bool(
            normalized["operation"] != "update"
            or normalized["statement_type"] != "update_review_state"
        )
        if missing_fields or transition_invalid or operation_invalid:
            invalid.append(
                {
                    "idempotency_key": normalized["idempotency_key"],
                    "missing_fields": missing_fields,
                    "transition_invalid": transition_invalid,
                    "operation_invalid": operation_invalid,
                }
            )
        payloads.append(normalized)
    return payloads, invalid


def _operator_summary(operator_evidence):
    operator_evidence = _as_dict(operator_evidence)
    return {
        "provided_keys": sorted(operator_evidence.keys()),
        "operator_confirmed_manual_shell_window": bool(
            operator_evidence.get("operator_confirmed_manual_shell_window")
        ),
        "operator_confirmed_post_update_inventory_planned": bool(
            operator_evidence.get("operator_confirmed_post_update_inventory_planned")
        ),
        "operator_confirmed_no_api_db_write": bool(
            operator_evidence.get("operator_confirmed_no_api_db_write")
        ),
        "operator_confirmed_no_scheduler_attach": bool(
            operator_evidence.get("operator_confirmed_no_scheduler_attach")
        ),
        "approval_token_submitted_to_api": _contains_forbidden_token_key(
            operator_evidence
        ),
    }


def build_candidate_queue_review_decision_writer_cli_plan(
    *,
    transaction_preview,
    operator_evidence=None,
    execute_requested=False,
    apply_real_write=False,
    approval_token=None,
    approval_token_secret=None,
    backup_verified=False,
    review_inventory_smoke_passed=False,
):
    """建立 review_state writer CLI gate；本階段仍不執行 update。"""
    transaction_preview = _as_dict(transaction_preview)
    operator_summary = _operator_summary(operator_evidence)
    statement_payloads, invalid_payloads = _statement_payloads(transaction_preview)
    approval_token_present = bool(approval_token)
    approval_token_secret_configured = bool(approval_token_secret)
    approval_token_valid = _approval_token_valid(approval_token, approval_token_secret)
    post_update_inventory_planned = bool(
        review_inventory_smoke_passed
        or operator_summary["operator_confirmed_post_update_inventory_planned"]
    )
    transaction_ready = bool(
        transaction_preview.get("mode")
        == "candidate_queue_review_decision_transaction_preview"
        and transaction_preview.get("transaction_ready")
    )
    writer_implementation_enabled = False
    payloads_valid = bool(statement_payloads and not invalid_payloads)
    gates = [
        {
            "key": "transaction_preview_ready",
            "label": "review_state transaction preview 必須通過",
            "passed": transaction_ready,
        },
        {
            "key": "transaction_has_update_statements",
            "label": "transaction preview 至少包含一筆 update_review_state statement",
            "passed": bool(statement_payloads),
        },
        {
            "key": "transaction_statement_payloads_valid",
            "label": "statement payload 必須包含 dedupe_key、目前狀態與下一狀態",
            "passed": payloads_valid,
        },
        {
            "key": "execute_requested",
            "label": "--execute flag 必須由 shell/CLI 明確提供",
            "passed": bool(execute_requested),
        },
        {
            "key": "apply_real_write_requested",
            "label": "--apply-real-write flag 必須由 shell/CLI 明確提供",
            "passed": bool(apply_real_write),
        },
        {
            "key": "approval_token_present",
            "label": f"{APPROVAL_ENV_VAR} approval token 必須只由 shell 提供",
            "passed": approval_token_present,
        },
        {
            "key": "approval_token_secret_configured",
            "label": f"{APPROVAL_ENV_VAR} 環境 token 必須只由 shell 載入",
            "passed": approval_token_secret_configured,
        },
        {
            "key": "approval_token_valid",
            "label": "approval token 必須符合 shell 環境 token",
            "passed": approval_token_valid,
        },
        {
            "key": "backup_verified",
            "label": "正式更新前必須確認最新備份已完成",
            "passed": bool(backup_verified),
        },
        {
            "key": "manual_shell_window_acknowledged",
            "label": "操作員確認只能在人工 shell 寫入窗口執行",
            "passed": bool(
                operator_summary["operator_confirmed_manual_shell_window"]
            ),
        },
        {
            "key": "post_update_inventory_planned",
            "label": "操作員確認正式更新後會跑只讀 inventory / smoke",
            "passed": post_update_inventory_planned,
        },
        {
            "key": "operator_confirmed_no_api_db_write_or_scheduler",
            "label": "操作員確認本 API 不寫 DB、不掛 scheduler",
            "passed": bool(
                operator_summary["operator_confirmed_no_api_db_write"]
                and operator_summary["operator_confirmed_no_scheduler_attach"]
            ),
        },
        {
            "key": "writer_no_token_submitted_to_api",
            "label": "operator_evidence 不得包含一次性 approval token key",
            "passed": not operator_summary["approval_token_submitted_to_api"],
        },
        {
            "key": "review_decision_writer_implementation_enabled",
            "label": "review_state writer 實作尚未啟用，本階段只提供 status gate",
            "passed": writer_implementation_enabled,
        },
        {
            "key": "crawler_stays_disabled",
            "label": "review_state writer 不掛 crawler 或 scheduler",
            "passed": True,
        },
    ]
    blocked_reasons = [gate["key"] for gate in gates if not gate["passed"]]

    return {
        "mode": "candidate_queue_review_decision_writer_cli_blocked",
        "target_table": TARGET_TABLE,
        "target_operation": "update_review_state",
        "execute_requested": bool(execute_requested),
        "apply_real_write_requested": bool(apply_real_write),
        "backup_verified": bool(backup_verified),
        "approval_token_present": approval_token_present,
        "approval_token_valid": approval_token_valid,
        "approval_env_var": APPROVAL_ENV_VAR,
        "approval_token_secret_configured": approval_token_secret_configured,
        "writer_implementation_enabled": writer_implementation_enabled,
        "ready_for_shell_command_authoring": bool(
            transaction_ready
            and payloads_valid
            and operator_summary["operator_confirmed_manual_shell_window"]
            and post_update_inventory_planned
            and not operator_summary["approval_token_submitted_to_api"]
        ),
        "ready_for_real_write": False,
        "ready_for_api_review_state_update": False,
        "ready_for_api_database_write": False,
        "ready_for_scheduler_attach": False,
        "review_state_update_allowed_from_api": False,
        "api_executes_cli": False,
        "api_reads_approval_token": False,
        "api_writes_file": False,
        "api_writes_database": False,
        "api_updates_review_state": False,
        "approval_record_written": False,
        "decision_record_written": False,
        "review_state_update_executed": False,
        "database_connection_opened": False,
        "database_session_created": False,
        "explicit_transaction_opened": False,
        "transaction_opened": False,
        "transaction_committed": False,
        "database_write_executed": False,
        "database_commit_executed": False,
        "database_rollback_executed": False,
        "external_network_executed": False,
        "scheduler_attached": False,
        "writes_executed": False,
        "would_write_database": False,
        "exit_code": 2 if execute_requested else 0,
        "blocked_reasons": blocked_reasons,
        "approval_gates": gates,
        "operator_writer_summary": operator_summary,
        "writer_payload_summary": {
            "statement_count": len(statement_payloads),
            "invalid_statement_count": len(invalid_payloads),
            "invalid_statements": invalid_payloads,
            "allowed_next_states": list(ALLOWED_DECISIONS),
        },
        "statement_summary": {
            "statement_count": len(statement_payloads),
            "invalid_statement_count": len(invalid_payloads),
            "allowed_next_states": list(ALLOWED_DECISIONS),
            "review_state_updates": [
                {
                    **payload,
                    "write_status": "blocked_writer_status_only",
                }
                for payload in statement_payloads
            ],
        },
        "script_path": "scripts/market_intel_review_decision_writer.py",
        "command_bundle": {
            "dry_run_command": (
                "python scripts/market_intel_review_decision_writer.py "
                "--transaction-json <payload.json>"
            ),
            "apply_command_shape": (
                "MARKET_INTEL_QUEUE_WRITE_APPROVAL=<token> "
                "python scripts/market_intel_review_decision_writer.py --execute "
                "--apply-real-write --transaction-json <payload.json>"
            ),
            "api_must_not_execute_command": True,
        },
        "transaction_preview_summary": {
            "mode": transaction_preview.get("mode"),
            "transaction_preview_created": bool(
                transaction_preview.get("transaction_preview_created")
            ),
            "transaction_ready": bool(transaction_preview.get("transaction_ready")),
            "statement_count": len(
                _as_list(transaction_preview.get("statements"))
            ),
            "api_write_allowed": False,
        },
        "transaction_preview": {
            "mode": transaction_preview.get("mode"),
            "target_table": transaction_preview.get("target_table"),
            "transaction_ready": bool(transaction_preview.get("transaction_ready")),
            "transaction_preview_created": bool(
                transaction_preview.get("transaction_preview_created")
            ),
        },
        "statement_payloads": statement_payloads,
        "rollback_plan": [
            {
                "key": "no_write_no_db_rollback_required",
                "label": "本階段未寫 DB，不需要 DB rollback",
            },
            {
                "key": "manual_review_state_reversal",
                "label": "正式更新後若需回退，必須依 dedupe_key 與審核證據人工稽核",
            },
        ],
        "safety_contract": {
            "refuses_api_execution": True,
            "refuses_execute_until_writer_enabled": True,
            "requires_independent_approval_token": True,
            "requires_backup_verified": True,
            "requires_post_update_inventory": True,
            "target_table": TARGET_TABLE,
        },
        "safe_boundaries": [
            "do_not_execute_review_state_writer_from_api",
            "do_not_execute_review_decision_writer_from_api",
            "do_not_read_approval_token_from_review_state_writer_api",
            "do_not_open_database_connection_from_review_state_writer_status",
            "do_not_commit_review_state_writer_status",
            "do_not_update_review_state_from_api_writer_status",
            "do_not_update_review_state_from_review_state_writer_status",
            "do_not_attach_scheduler_from_review_state_writer",
            "no_remove_orphans",
            "no_momo_db_lifecycle_change",
        ],
    }
