"""AI Agent LOG controlled writeback consumer apply.

Consumes ready metadata-only dispatch receipts into a consumer-context receipt
ledger. This is an idempotent controlled apply step: it writes receipt metadata
to automation_operation_log so KM / RAG / PlayBook / MCP / verifier / AI Agent
loops can read a stable handoff, without persisting raw logs, calling tools,
triggering workflows, sending Telegram, or reading secrets.
"""

from __future__ import annotations

import asyncio
import json
from typing import Any

from sqlalchemy import text

from src.core.logging import get_logger
from src.db.base import get_db_context
from src.services.ai_agent_log_controlled_writeback_consumer_readback import (
    CONSUMER_EXECUTOR_ROUTE,
    CONSUMER_OPERATION_TYPE,
    DEFAULT_PROJECT_ID,
    load_latest_ai_agent_log_controlled_writeback_consumer_readback,
)
from src.services.ai_agent_log_controlled_writeback_dispatch import (
    FALLBACK_LEDGER_OPERATION_TYPE,
)

SCHEMA_VERSION = "ai_agent_log_controlled_writeback_consumer_apply_receipt_v1"
_DB_RETRY_DELAYS_SECONDS = (0.0, 0.25, 1.0)
logger = get_logger(__name__)


async def consume_latest_ai_agent_log_controlled_writeback(
    *,
    project_id: str = DEFAULT_PROJECT_ID,
) -> dict[str, Any]:
    """Persist idempotent consumer-context receipts for ready LOG bindings."""

    readback = await load_latest_ai_agent_log_controlled_writeback_consumer_readback(
        project_id=project_id,
    )
    receipt = build_ai_agent_log_controlled_writeback_consumer_apply_receipt(readback)
    if receipt["active_blockers"]:
        receipt["apply_result"] = {
            "operation_type": CONSUMER_OPERATION_TYPE,
            "ledger_operation_type": None,
            "semantic_operation_type": CONSUMER_OPERATION_TYPE,
            "db_write_status": "not_attempted",
            "consumer_apply_receipt_row_count": 0,
            "reason": "active_blockers_present",
        }
        return receipt

    try:
        ledger_operation_type, rows = await _write_consumer_receipts_with_retry(
            project_id=project_id,
            receipt=receipt,
        )
    except Exception as exc:  # pragma: no cover - live DB pool pressure
        logger.warning(
            "log_controlled_writeback_consumer_apply_db_unavailable",
            project_id=project_id,
            error_type=type(exc).__name__,
        )
        receipt["status"] = "blocked_waiting_controlled_writeback_consumer_apply_db"
        receipt["active_blockers"] = _unique(
            [
                *receipt.get("active_blockers", []),
                "consumer_apply_db_write_unavailable",
            ]
        )
        receipt["apply_result"] = {
            "operation_type": CONSUMER_OPERATION_TYPE,
            "ledger_operation_type": None,
            "semantic_operation_type": CONSUMER_OPERATION_TYPE,
            "db_write_status": "unavailable",
            "error_type": type(exc).__name__,
            "consumer_apply_receipt_row_count": 0,
            "rows": [],
        }
        return receipt

    inserted_count = sum(1 for row in rows if row["created"] is True)
    existing_count = sum(1 for row in rows if row["created"] is False)
    receipt["apply_result"] = {
        "operation_type": CONSUMER_OPERATION_TYPE,
        "ledger_operation_type": ledger_operation_type,
        "semantic_operation_type": CONSUMER_OPERATION_TYPE,
        "inserted_count": inserted_count,
        "existing_count": existing_count,
        "consumer_apply_receipt_row_count": len(rows),
        "rows": rows,
    }
    receipt["rollups"]["runtime_target_write_performed"] = True
    receipt["rollups"]["consumer_apply_receipt_row_count"] = len(rows)
    receipt["rollups"]["inserted_consumer_apply_receipt_row_count"] = inserted_count
    receipt["rollups"]["existing_consumer_apply_receipt_row_count"] = existing_count
    receipt["operation_boundaries"]["consumer_context_receipt_write_performed"] = True
    receipt["operation_boundaries"]["runtime_target_write_performed"] = True
    return receipt


async def _write_consumer_receipts_with_retry(
    *,
    project_id: str,
    receipt: dict[str, Any],
) -> tuple[str, list[dict[str, Any]]]:
    last_error: Exception | None = None
    for attempt, delay_seconds in enumerate(_DB_RETRY_DELAYS_SECONDS, start=1):
        if delay_seconds:
            await asyncio.sleep(delay_seconds)
        try:
            return await _write_consumer_receipts_once(
                project_id=project_id,
                receipt=receipt,
            )
        except Exception as exc:  # pragma: no cover - exercised by live pressure
            last_error = exc
            logger.warning(
                "log_controlled_writeback_consumer_apply_attempt_failed",
                project_id=project_id,
                attempt=attempt,
                error_type=type(exc).__name__,
            )
    if last_error is not None:
        raise last_error
    return FALLBACK_LEDGER_OPERATION_TYPE, []


async def _write_consumer_receipts_once(
    *,
    project_id: str,
    receipt: dict[str, Any],
) -> tuple[str, list[dict[str, Any]]]:
    rows = []
    async with get_db_context(project_id) as db:
        await db.execute(text("SET LOCAL statement_timeout = '5000ms'"), {})
        ledger_operation_type = await _resolve_ledger_operation_type(db)
        for item in receipt["consumer_apply_receipts"]:
            rows.append(
                await _insert_consumer_receipt_row(
                    db,
                    project_id=project_id,
                    item=item,
                    ledger_operation_type=ledger_operation_type,
                )
            )
    return ledger_operation_type, rows


def build_ai_agent_log_controlled_writeback_consumer_apply_receipt(
    readback: dict[str, Any],
) -> dict[str, Any]:
    """Build a deterministic controlled-apply envelope from consumer readback."""

    bindings = [
        binding
        for binding in readback.get("consumer_bindings", [])
        if isinstance(binding, dict)
    ]
    apply_receipts = [_consumer_apply_receipt(binding) for binding in bindings]
    active_blockers = _active_blockers(
        readback=readback,
        bindings=bindings,
        apply_receipts=apply_receipts,
    )
    return {
        "schema_version": SCHEMA_VERSION,
        "priority": "P1-LOG-KM-RAG-MCP-PLAYBOOK",
        "scope": "ai_agent_log_controlled_writeback_consumer_apply",
        "status": (
            "controlled_writeback_consumer_apply_ready"
            if not active_blockers
            else "blocked_waiting_controlled_writeback_consumer_apply_inputs"
        ),
        "readback": {
            "workplan_id": "P1-LOG-CONTROLLED-WRITEBACK-CONSUMER-APPLY",
            "workplan_title": (
                "LOG metadata dispatch receipts consumed into KM / RAG / PlayBook / "
                "MCP / verifier / AI Agent context receipt ledger"
            ),
            "source_schema_version": readback.get("schema_version"),
            "source_status": readback.get("status"),
            "operation_type": CONSUMER_OPERATION_TYPE,
            "executor_route": CONSUMER_EXECUTOR_ROUTE,
            "safe_next_step": (
                "post_apply_verify_consumer_context_receipts_then_read_runtime_control"
            ),
        },
        "consumer_apply_receipts": apply_receipts,
        "rollups": {
            "source_consumer_binding_count": len(bindings),
            "ready_source_consumer_binding_count": sum(
                1
                for binding in bindings
                if binding.get("status") == "ready_for_consumer_context"
            ),
            "target_count": 6,
            "consumer_apply_receipt_count": len(apply_receipts),
            "ready_consumer_apply_receipt_count": sum(
                1 for item in apply_receipts if item["status"] == "ready_for_context_receipt_write"
            ),
            "metadata_only_receipt_count": sum(
                1 for item in apply_receipts if item["raw_payload_included"] is False
            ),
            "post_apply_verifier_ref_count": sum(
                len(item["post_apply_verifier_refs"]) for item in apply_receipts
            ),
            "runtime_target_write_performed": False,
            "consumer_apply_receipt_row_count": 0,
            "inserted_consumer_apply_receipt_row_count": 0,
            "existing_consumer_apply_receipt_row_count": 0,
        },
        "active_blockers": active_blockers,
        "operation_boundaries": {
            "metadata_ledger_write_only": True,
            "consumer_context_receipt_write_performed": False,
            "runtime_target_write_performed": False,
            "km_write_performed": False,
            "rag_index_write_performed": False,
            "playbook_trust_write_performed": False,
            "mcp_tool_call_performed": False,
            "agent_runtime_action_performed": False,
            "telegram_send_performed": False,
            "workflow_trigger_performed": False,
            "raw_log_payload_persisted": False,
            "secret_value_collection_allowed": False,
            "github_api_used": False,
        },
    }


def _consumer_apply_receipt(binding: dict[str, Any]) -> dict[str, Any]:
    target = str(binding.get("target") or "")
    dispatch_receipt_id = str(binding.get("dispatch_receipt_id") or "")
    verifier_refs = _strings(
        (binding.get("post_apply_verifier") or {}).get("verifier_refs")
        or binding.get("post_apply_verifier_refs")
    )
    return {
        "consumer_receipt_id": f"{CONSUMER_OPERATION_TYPE}::{target}",
        "source_dispatch_receipt_id": dispatch_receipt_id,
        "target": target,
        "target_surface": str(binding.get("target_surface") or ""),
        "consumer_surface": str(binding.get("consumer_surface") or ""),
        "risk_tier": str(binding.get("risk_tier") or ""),
        "executor_route": CONSUMER_EXECUTOR_ROUTE,
        "status": (
            "ready_for_context_receipt_write"
            if _binding_ready_for_apply(binding, verifier_refs)
            else "blocked_waiting_context_receipt_controls"
        ),
        "source_ledger_op_id": str(binding.get("ledger_op_id") or ""),
        "post_apply_verifier_refs": verifier_refs,
        "raw_payload_included": bool(binding.get("raw_payload_included") is not False),
        "target_selector": {
            "dispatch_receipt_id": dispatch_receipt_id,
            "target": target,
            "target_surface": str(binding.get("target_surface") or ""),
            "consumer_surface": str(binding.get("consumer_surface") or ""),
        },
        "source_of_truth_diff": {
            "current_state": "consumer_binding_ready",
            "desired_state": "consumer_context_receipt_recorded",
            "delta_kind": f"{target}_consumer_context_receipt",
            "raw_payload_included": False,
        },
        "check_mode": {
            "enabled": True,
            "checks": [
                "source_dispatch_receipt_present",
                "consumer_surface_present",
                "metadata_only_raw_payload_absent",
                "post_apply_verifier_refs_present",
                "source_binding_ready",
            ],
        },
        "rollback": {
            "required": True,
            "rollback_ref": (
                "rollback://ai-agent-log-controlled-writeback-consumer-apply/"
                f"{CONSUMER_OPERATION_TYPE}::{target}"
            ),
            "strategy": "mark_consumer_context_receipt_superseded_and_ignore",
        },
    }


def _binding_ready_for_apply(
    binding: dict[str, Any],
    verifier_refs: list[str],
) -> bool:
    if binding.get("status") != "ready_for_consumer_context":
        return False
    if str(binding.get("dispatch_receipt_id") or "") == "":
        return False
    if str(binding.get("target") or "") == "":
        return False
    if str(binding.get("target_surface") or "") == "":
        return False
    if str(binding.get("consumer_surface") or "") == "":
        return False
    if binding.get("raw_payload_included") is not False:
        return False
    return bool(verifier_refs)


def _active_blockers(
    *,
    readback: dict[str, Any],
    bindings: list[dict[str, Any]],
    apply_receipts: list[dict[str, Any]],
) -> list[str]:
    blockers: list[str] = []
    if readback.get("status") != "controlled_writeback_consumer_readback_ready":
        blockers.append("controlled_writeback_consumer_readback_not_ready")
    if (readback.get("controlled_consume") or {}).get("controlled_consume_allowed") is not True:
        blockers.append("controlled_consume_not_allowed")
    if readback.get("active_blockers"):
        blockers.extend(str(item) for item in readback.get("active_blockers") or [])
    if not bindings:
        blockers.append("consumer_bindings_missing")
    for item in apply_receipts:
        target = item["target"] or "unknown"
        if item["status"] != "ready_for_context_receipt_write":
            blockers.append(f"{target}_consumer_apply_receipt_not_ready")
        if item["raw_payload_included"] is not False:
            blockers.append(f"{target}_raw_payload_included")
    return _unique(blockers)


async def _insert_consumer_receipt_row(
    db: Any,
    *,
    project_id: str,
    item: dict[str, Any],
    ledger_operation_type: str,
) -> dict[str, Any]:
    result = await db.execute(
        text("""
            INSERT INTO automation_operation_log (
                operation_type, actor, status,
                input, output, dry_run_result, tags
            )
            SELECT
                :ledger_operation_type,
                :actor,
                'success',
                CAST(:input AS jsonb),
                CAST(:output AS jsonb),
                CAST(:dry_run_result AS jsonb),
                :tags
            WHERE NOT EXISTS (
                SELECT 1
                FROM automation_operation_log existing
                WHERE coalesce(
                    existing.input ->> 'semantic_operation_type',
                    existing.operation_type
                ) = :operation_type
                  AND existing.input ->> 'consumer_receipt_id' = :consumer_receipt_id
            )
            RETURNING op_id::text
        """),
        {
            "operation_type": CONSUMER_OPERATION_TYPE,
            "ledger_operation_type": ledger_operation_type,
            "actor": CONSUMER_EXECUTOR_ROUTE,
            "consumer_receipt_id": item["consumer_receipt_id"],
            "input": json.dumps(
                {
                    "schema_version": SCHEMA_VERSION,
                    "project_id": project_id,
                    "semantic_operation_type": CONSUMER_OPERATION_TYPE,
                    "ledger_operation_type": ledger_operation_type,
                    "consumer_receipt_id": item["consumer_receipt_id"],
                    "source_dispatch_receipt_id": item["source_dispatch_receipt_id"],
                    "target": item["target"],
                    "target_surface": item["target_surface"],
                    "consumer_surface": item["consumer_surface"],
                    "risk_tier": item["risk_tier"],
                    "source_ledger_op_id": item["source_ledger_op_id"],
                    "runtime_target_write_performed": True,
                    "raw_payload_included": False,
                },
                ensure_ascii=False,
            ),
            "output": json.dumps(
                {
                    "executor_route": CONSUMER_EXECUTOR_ROUTE,
                    "semantic_operation_type": CONSUMER_OPERATION_TYPE,
                    "ledger_operation_type": ledger_operation_type,
                    "consumer_context_receipt_recorded": True,
                    "target_context_receipt_write_performed": True,
                    "runtime_target_write_performed": True,
                    "post_apply_verifier_refs": item["post_apply_verifier_refs"],
                    "next_action": "readback_consumer_context_receipts",
                    "km_write_performed": False,
                    "rag_index_write_performed": False,
                    "playbook_trust_write_performed": False,
                    "mcp_tool_call_performed": False,
                    "telegram_send_performed": False,
                },
                ensure_ascii=False,
            ),
            "dry_run_result": json.dumps(
                {
                    "target_selector": item["target_selector"],
                    "source_of_truth_diff": item["source_of_truth_diff"],
                    "check_mode": item["check_mode"],
                    "rollback": item["rollback"],
                    "post_apply_verifier_ref_count": len(item["post_apply_verifier_refs"]),
                    "raw_payload_included": False,
                },
                ensure_ascii=False,
            ),
            "tags": [
                "ai_agent",
                "log_feedback",
                "controlled_writeback_consumer",
                item["target"],
            ],
        },
    )
    op_id = result.scalar()
    if op_id:
        return {
            "consumer_receipt_id": item["consumer_receipt_id"],
            "target": item["target"],
            "created": True,
            "op_id": str(op_id),
        }

    existing = await db.execute(
        text("""
            SELECT op_id::text
            FROM automation_operation_log
            WHERE coalesce(input ->> 'semantic_operation_type', operation_type)
                = :operation_type
              AND input ->> 'consumer_receipt_id' = :consumer_receipt_id
            ORDER BY created_at DESC
            LIMIT 1
        """),
        {
            "operation_type": CONSUMER_OPERATION_TYPE,
            "consumer_receipt_id": item["consumer_receipt_id"],
        },
    )
    return {
        "consumer_receipt_id": item["consumer_receipt_id"],
        "target": item["target"],
        "created": False,
        "op_id": str(existing.scalar() or ""),
    }


async def _resolve_ledger_operation_type(db: Any) -> str:
    result = await db.execute(
        text("""
            SELECT CASE
                WHEN EXISTS (
                    SELECT 1
                    FROM pg_constraint
                    WHERE conname = 'automation_operation_log_type_valid'
                      AND pg_get_constraintdef(oid) LIKE '%' || :operation_type || '%'
                )
                THEN :operation_type
                ELSE :fallback_operation_type
            END
        """),
        {
            "operation_type": CONSUMER_OPERATION_TYPE,
            "fallback_operation_type": FALLBACK_LEDGER_OPERATION_TYPE,
        },
    )
    resolved = str(result.scalar() or "")
    if resolved == CONSUMER_OPERATION_TYPE:
        return CONSUMER_OPERATION_TYPE
    return FALLBACK_LEDGER_OPERATION_TYPE


def _strings(value: Any) -> list[str]:
    if isinstance(value, list):
        return [str(item) for item in value]
    if isinstance(value, tuple):
        return [str(item) for item in value]
    return []


def _unique(values: list[str]) -> list[str]:
    seen = set()
    result = []
    for value in values:
        if value in seen:
            continue
        seen.add(value)
        result.append(value)
    return result
