"""
pgdag.build() — One-call wiring for the entire execution layer.

    from pgdag import build

    layer = build(sbn_client=sbn, domain="finance.payroll")

    layer.runner.register_steps({"reserve_float": my_fn, ...})
    result = await layer.runner.execute(template, inputs={...})

That's it.  Everything is wired: seal backend, idempotency gate,
envelope builder, evidence chains, entity reducers, handoff helpers.

Without SBN (local dev / test):

    layer = build(domain="test")

All sealing falls back to SHA-256.  No network calls.  Same API.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, Optional

from .chains import EvidenceChainManager, ChainHealthTracker
from .dag import PGDAGRunner
from .entity import EntityReducer
from .envelope import SnapChoreCapture, StepEnvelopeBuilder
from .gate import IdempotencyGate
from .handoff import HandoffEnvelope, build_handoff
from .interrupt import InterruptionHandler
from .runner import LocalSealBackend, ProofWriter, SbnSealBackend


# ---------------------------------------------------------------------------
# ExecutionLayer — everything build() returns
# ---------------------------------------------------------------------------

@dataclass
class ExecutionLayer:
    """Pre-wired execution layer returned by build().

    Attributes:
        runner:      PGDAGRunner — register steps, execute templates.
        chains:      EvidenceChainManager — append-only inter-run proof chains.
        chain_health: ChainHealthTracker — verify chain integrity.
        gate:        IdempotencyGate — content-addressed dedup (shared with runner).
        interruption: InterruptionHandler — inject governance signals (shared with runner).
        writer:      ProofWriter — seal envelopes outside the runner if needed.
        domain:      The domain string this layer was built for.
        spec_version: Default spec version for this layer.

    Methods:
        entity(type, id) — create an EntityReducer for cross-run tracking.
        handoff(...)     — build a HandoffEnvelope for cross-product bridging.
    """
    runner: PGDAGRunner
    chains: EvidenceChainManager
    chain_health: ChainHealthTracker
    gate: IdempotencyGate
    interruption: InterruptionHandler
    writer: ProofWriter
    domain: str = "general"
    spec_version: str = "1.0"
    _snapchore: Any = field(default=None, repr=False)

    def entity(self, entity_type: str, entity_id: str) -> EntityReducer:
        """Create an EntityReducer for cross-run state tracking.

        Usage:
            worker = layer.entity("employee", "worker-42")
            state = worker.fold_run("run-123", proofs, amount=1500.00)
            print(state.total_runs, state.total_amount)
        """
        return EntityReducer(
            entity_type=entity_type,
            entity_id=entity_id,
            snapchore=self._snapchore,
        )

    def handoff(
        self,
        *,
        source_product: str,
        source_template: str,
        source_run_id: str,
        source_slot_id: str = "",
        source_merkle_root: str = "",
        target_product: str = "",
        target_template: str = "",
        payload: Optional[Dict[str, Any]] = None,
    ) -> HandoffEnvelope:
        """Build a HandoffEnvelope for cross-product bridging.

        Usage:
            handoff = layer.handoff(
                source_product="dominion",
                source_template="payroll_batch",
                source_run_id=result.run_id,
                source_merkle_root=result.merkle_root_hash,
                target_product="sonic",
                target_template="settlement_payout",
                payload={"batch_id": "batch-42", "total": 15000.00},
            )
        """
        return build_handoff(
            source_product=source_product,
            source_template=source_template,
            source_run_id=source_run_id,
            source_slot_id=source_slot_id,
            source_merkle_root=source_merkle_root,
            target_product=target_product,
            target_template=target_template,
            payload=payload,
            snapchore=self._snapchore,
        )


# ---------------------------------------------------------------------------
# build() — the one-call factory
# ---------------------------------------------------------------------------

def build(
    *,
    sbn_client: Any = None,
    domain: str = "general",
    spec_version: str = "1.0",
) -> ExecutionLayer:
    """Build a fully wired PG-DAG execution layer.

    Args:
        sbn_client:   Optional SBN SDK client (with .snapchore, .gateway, .blocks).
                      If None, everything runs locally with SHA-256 sealing.
        domain:       Default domain string for this product's execution
                      (e.g., "finance.payroll", "treasury.execution", "settlement").
        spec_version: Default spec version (default "1.0").

    Returns:
        ExecutionLayer with all components pre-wired.

    Examples:

        # Production (SBN-connected):
        from sbn import SBNClient
        from pgdag import build

        sbn = SBNClient(api_key="...", project_id="...")
        layer = build(sbn_client=sbn, domain="finance.payroll")

        layer.runner.register_steps({
            "ingest_timesheet": ingest_fn,
            "compute_gross":    compute_fn,
            "reserve_float":    reserve_fn,
            "execute_transfer": transfer_fn,
        })

        result = await layer.runner.execute(template, inputs={...}, actor="dom-engine")

        # Local dev (no SBN):
        layer = build(domain="test")
        # Same API, SHA-256 sealing, no network calls.
    """
    # Extract sub-clients
    snapchore = None
    gateway = None
    blocks = None

    if sbn_client is not None:
        snapchore = getattr(sbn_client, "snapchore", None)
        gateway = getattr(sbn_client, "gateway", None)
        blocks = getattr(sbn_client, "blocks", None)

    # -- Seal backend --
    if snapchore is not None:
        _sbn_obj = type("_SBN", (), {"snapchore": snapchore, "blocks": blocks})()
        seal_backend = SbnSealBackend(sbn_client=_sbn_obj)
    else:
        seal_backend = LocalSealBackend()

    # -- Envelope builder --
    envelope_builder = StepEnvelopeBuilder(
        snapchore=snapchore if isinstance(snapchore, SnapChoreCapture) else None,
    )

    # -- Shared components --
    gate = IdempotencyGate(snapchore=snapchore)
    writer = ProofWriter(backend=seal_backend)
    interruption = InterruptionHandler()

    # -- Evidence chains --
    chains = EvidenceChainManager(snapchore=snapchore, sbn_client=sbn_client)
    chain_health = ChainHealthTracker(manager=chains)

    # -- PGDAGRunner --
    runner = PGDAGRunner(
        snapchore=snapchore,
        gateway=gateway,
        blocks=blocks,
        domain=domain,
        spec_version=spec_version,
    )

    # Share the same gate and interruption handler with the runner
    # (override the ones the runner created internally)
    runner._gate = gate
    runner._interruption = interruption
    runner._writer = writer
    runner._envelope_builder = envelope_builder

    return ExecutionLayer(
        runner=runner,
        chains=chains,
        chain_health=chain_health,
        gate=gate,
        interruption=interruption,
        writer=writer,
        domain=domain,
        spec_version=spec_version,
        _snapchore=snapchore,
    )
