"""Causal node — the fundamental unit of a Causal Tensor Network."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Callable, Optional

import numpy as np
from numpy.typing import NDArray


@dataclass
class CausalNode:
    """A node in a Causal Tensor Network.

    Each node sits at a specific time coordinate and holds a state vector.
    It may receive influences from both forward-causal parents (past to future)
    and retrocausal parents (future to past), creating closed causal loops.

    Attributes:
        node_id: Unique identifier for this node.
        time_coordinate: Position on the temporal axis.
        state_dim: Dimensionality of the state vector.
        state: Current state vector. Initialized to small random values if not provided.
        causal_parents: List of (parent_id, weight) for forward-causal connections.
        retrocausal_parents: List of (parent_id, weight) for backward-time connections.
        forward_fn: Optional custom transformation for forward-causal inputs.
        retro_fn: Optional custom transformation for retrocausal inputs.
        paradox_pressure: Magnitude of causal inconsistency at this node.
    """

    node_id: str
    time_coordinate: float
    state_dim: int
    state: NDArray[np.floating] = field(default_factory=lambda: np.array([]))

    causal_parents: list[tuple[str, float]] = field(default_factory=list)
    retrocausal_parents: list[tuple[str, float]] = field(default_factory=list)

    forward_fn: Optional[Callable[[NDArray], NDArray]] = None
    retro_fn: Optional[Callable[[NDArray], NDArray]] = None

    paradox_pressure: float = 0.0

    def __post_init__(self) -> None:
        if len(self.state) == 0:
            self.state = np.random.randn(self.state_dim) * 0.1
