"""TQCE Orchestrator — the full pipeline coordinator.

Workflow:
    1. Build a Causal Tensor Network representing your problem
    2. Superpose multiple candidate timelines (TEC)
    3. Run interference to find the most consistent candidate
    4. Use PPGD to optimize toward exact self-consistency
    5. Optionally use Bootstrap Amplification on the result

Use cases:
    - Constraint satisfaction with circular dependencies
    - Self-referential optimization problems
    - Signal amplification with stability guarantees
    - Causal reasoning in systems with feedback loops
    - Temporal data anomaly detection
"""

from __future__ import annotations

from typing import Any

import networkx as nx
import numpy as np
from numpy.typing import NDArray

from tqce.network.ctn import CausalTensorNetwork
from tqce.optimization.ppgd import ParadoxPressureGradientDescent
from tqce.quantum.wavefunction import TemporalWavefunction


class TQCEOrchestrator:
    """Full TQCE solve pipeline.

    Args:
        problem_name: Human-readable name for this problem instance.
    """

    def __init__(self, problem_name: str = "Unnamed") -> None:
        self.problem_name = problem_name
        self.ctn = CausalTensorNetwork()
        self.ppgd: ParadoxPressureGradientDescent | None = None
        self.wavefunction: TemporalWavefunction | None = None
        self.results: dict[str, Any] = {}

    def build_network(self) -> CausalTensorNetwork:
        """Return the internal CTN for manual construction."""
        return self.ctn

    def solve(
        self,
        n_candidate_timelines: int = 20,
        use_tec: bool = True,
        use_ppgd: bool = True,
        verbose: bool = False,
    ) -> dict[str, Any]:
        """Run the full TQCE solve pipeline.

        Args:
            n_candidate_timelines: Number of timelines for TEC phase.
            use_tec: Enable Temporal Eigenstate Collapse phase.
            use_ppgd: Enable PPGD refinement phase.
            verbose: Print progress to stdout.

        Returns:
            Dict with keys: fixed_point, tec (optional), ppgd (optional), summary.
        """
        if verbose:
            print(f"\n{'=' * 50}")
            print(f"  TQCE Solving: {self.problem_name}")
            print(f"{'=' * 50}\n")

        results: dict[str, Any] = {}

        # Phase 1: Temporal Fixed-Point
        if verbose:
            print("Phase 1: Temporal Fixed-Point Iteration")
        fp_result = self.ctn.run_temporal_fixed_point(verbose=verbose)
        results["fixed_point"] = fp_result

        # Phase 2: Temporal Eigenstate Collapse
        if use_tec and self.ctn.nodes:
            if verbose:
                print("\nPhase 2: Temporal Eigenstate Collapse")

            first_node = next(iter(self.ctn.nodes.values()))
            state_dim = first_node.state_dim

            self.wavefunction = TemporalWavefunction(
                n_timelines=n_candidate_timelines, state_dim=state_dim
            )

            def consistency_fn(state: NDArray) -> float:
                total = 0.0
                for node in self.ctn.nodes.values():
                    if len(state) == node.state_dim:
                        old_state = node.state.copy()
                        node.state = state
                        expected = self.ctn.compute_node_state(node.node_id)
                        node.state = old_state
                        total -= float(np.linalg.norm(state - expected))
                return total

            best_timeline, best_consistency = self.wavefunction.collapse(
                consistency_fn, n_interference_steps=15
            )
            results["tec"] = {
                "best_timeline": best_timeline,
                "consistency_score": best_consistency,
                "n_timelines": n_candidate_timelines,
            }
            if verbose:
                print(
                    f"  Best timeline consistency score: {best_consistency:.4f}"
                )

        # Phase 3: PPGD Refinement
        if use_ppgd:
            if verbose:
                print("\nPhase 3: Paradox Pressure Gradient Descent")

            self.ppgd = ParadoxPressureGradientDescent(
                self.ctn, lr=0.005, cer_lambda=0.05
            )
            ppgd_result = self.ppgd.optimize(
                max_steps=500, target_loss=1e-5, verbose=verbose
            )
            results["ppgd"] = ppgd_result

        # Summary
        results["summary"] = {
            "problem": self.problem_name,
            "n_nodes": len(self.ctn.nodes),
            "n_causal_edges": len(self.ctn.causal_edges),
            "n_retrocausal_edges": len(self.ctn.retro_edges),
            "has_closed_causal_loops": len(
                list(nx.simple_cycles(self.ctn.graph))
            )
            > 0,
            "final_node_states": {
                nid: n.state.tolist() for nid, n in self.ctn.nodes.items()
            },
        }

        self.results = results
        return results
