from typing import List, Dict, Any, Optional

class QuantumCircuit:
    """
    Object-oriented builder for AnuAstra quantum physics execution.
    Mirrors the industry-standard DX of IBM Qiskit and Google Cirq.

    Supported Gates (matching the AnuAstra VQP server gate set):
    ─────────────────────────────────────────────────────────────
    Single-qubit  : H, X, Y, Z, S, Sdg, T, Tdg, SX, SXdg,
                    RX(θ), RY(θ), RZ(θ), P(λ), U1(λ), U2(φ,λ), U3(θ,φ,λ)
    Two-qubit     : CNOT/CX, CZ, CY, CH, SWAP, iSWAP,
                    CRX(θ), CRY(θ), CRZ(θ), CP(λ)
    Three-qubit   : CCX/Toffoli, CSWAP/Fredkin, RCCX
    """

    def __init__(self, qubits: int):
        if qubits <= 0:
            raise ValueError("A QuantumCircuit must have at least 1 qubit.")
        self.qubits = qubits
        self.operations: List[Dict[str, Any]] = []

    def _validate_qubit(self, q: int):
        if q < 0 or q >= self.qubits:
            raise ValueError(f"Qubit index {q} is out of bounds for a {self.qubits}-qubit circuit.")

    def _val2(self, c: int, t: int):
        self._validate_qubit(c)
        self._validate_qubit(t)
        if c == t:
            raise ValueError("Control and Target qubits cannot be the same.")

    def _val3(self, c1: int, c2: int, t: int):
        self._validate_qubit(c1)
        self._validate_qubit(c2)
        self._validate_qubit(t)

    # ─────────────────────────────────────────────────────────────
    #  SINGLE-QUBIT GATES — PAULI
    # ─────────────────────────────────────────────────────────────

    def x(self, target: int) -> 'QuantumCircuit':
        """Pauli-X (NOT) gate — flips qubit state."""
        self._validate_qubit(target)
        self.operations.append({"gate": "X", "target": [target]})
        return self

    def y(self, target: int) -> 'QuantumCircuit':
        """Pauli-Y gate."""
        self._validate_qubit(target)
        self.operations.append({"gate": "Y", "target": [target]})
        return self

    def z(self, target: int) -> 'QuantumCircuit':
        """Pauli-Z gate (phase flip)."""
        self._validate_qubit(target)
        self.operations.append({"gate": "Z", "target": [target]})
        return self

    # ─────────────────────────────────────────────────────────────
    #  SINGLE-QUBIT GATES — HADAMARD
    # ─────────────────────────────────────────────────────────────

    def h(self, target: int) -> 'QuantumCircuit':
        """Hadamard gate — creates superposition."""
        self._validate_qubit(target)
        self.operations.append({"gate": "H", "target": [target]})
        return self

    # ─────────────────────────────────────────────────────────────
    #  SINGLE-QUBIT GATES — PHASE
    # ─────────────────────────────────────────────────────────────

    def s(self, target: int) -> 'QuantumCircuit':
        """S gate (√Z, phase gate π/2)."""
        self._validate_qubit(target)
        self.operations.append({"gate": "S", "target": [target]})
        return self

    def sdg(self, target: int) -> 'QuantumCircuit':
        """S-dagger gate (S†, inverse phase -π/2)."""
        self._validate_qubit(target)
        self.operations.append({"gate": "Sdg", "target": [target]})
        return self

    def t(self, target: int) -> 'QuantumCircuit':
        """T gate (√S, phase gate π/4)."""
        self._validate_qubit(target)
        self.operations.append({"gate": "T", "target": [target]})
        return self

    def tdg(self, target: int) -> 'QuantumCircuit':
        """T-dagger gate (T†, inverse phase -π/4)."""
        self._validate_qubit(target)
        self.operations.append({"gate": "Tdg", "target": [target]})
        return self

    def p(self, target: int, lam: float) -> 'QuantumCircuit':
        """Phase gate P(λ) — phase rotation by λ radians."""
        self._validate_qubit(target)
        self.operations.append({"gate": "P", "target": [target], "param": float(lam)})
        return self

    # ─────────────────────────────────────────────────────────────
    #  SINGLE-QUBIT GATES — ROTATION
    # ─────────────────────────────────────────────────────────────

    def rx(self, target: int, theta: float) -> 'QuantumCircuit':
        """RX(θ) — rotation around X-axis by θ radians."""
        self._validate_qubit(target)
        self.operations.append({"gate": "RX", "target": [target], "param": float(theta)})
        return self

    def ry(self, target: int, theta: float) -> 'QuantumCircuit':
        """RY(θ) — rotation around Y-axis by θ radians."""
        self._validate_qubit(target)
        self.operations.append({"gate": "RY", "target": [target], "param": float(theta)})
        return self

    def rz(self, target: int, theta: float) -> 'QuantumCircuit':
        """RZ(θ) — rotation around Z-axis by θ radians."""
        self._validate_qubit(target)
        self.operations.append({"gate": "RZ", "target": [target], "param": float(theta)})
        return self

    # ─────────────────────────────────────────────────────────────
    #  SINGLE-QUBIT GATES — SQRT
    # ─────────────────────────────────────────────────────────────

    def sx(self, target: int) -> 'QuantumCircuit':
        """SX gate (√X) — half-way NOT gate."""
        self._validate_qubit(target)
        self.operations.append({"gate": "SX", "target": [target]})
        return self

    def sxdg(self, target: int) -> 'QuantumCircuit':
        """SX-dagger gate (√X†)."""
        self._validate_qubit(target)
        self.operations.append({"gate": "SXdg", "target": [target]})
        return self

    # ─────────────────────────────────────────────────────────────
    #  SINGLE-QUBIT GATES — UNIVERSAL
    # ─────────────────────────────────────────────────────────────

    def u3(self, target: int, theta: float, phi: float, lam: float) -> 'QuantumCircuit':
        """U3(θ,φ,λ) — most general single-qubit unitary gate."""
        self._validate_qubit(target)
        self.operations.append({"gate": "U3", "target": [target], "params": [float(theta), float(phi), float(lam)]})
        return self

    def u2(self, target: int, phi: float, lam: float) -> 'QuantumCircuit':
        """U2(φ,λ) — IBM standard single-qubit gate (θ=π/2)."""
        self._validate_qubit(target)
        self.operations.append({"gate": "U2", "target": [target], "params": [float(phi), float(lam)]})
        return self

    def u1(self, target: int, lam: float) -> 'QuantumCircuit':
        """U1(λ) — single-qubit phase gate (alias for P)."""
        return self.p(target, lam)

    # ─────────────────────────────────────────────────────────────
    #  TWO-QUBIT GATES — CONTROLLED PAULI
    # ─────────────────────────────────────────────────────────────

    def cx(self, control: int, target: int) -> 'QuantumCircuit':
        """CX / CNOT gate — controlled-NOT. Industry standard alias."""
        return self.cnot(control, target)

    def cnot(self, control: int, target: int) -> 'QuantumCircuit':
        """CNOT gate — controlled-NOT."""
        self._val2(control, target)
        self.operations.append({"gate": "CNOT", "target": [control, target]})
        return self

    def cz(self, control: int, target: int) -> 'QuantumCircuit':
        """CZ gate — controlled-Z (phase flip on target when control=|1⟩)."""
        self._val2(control, target)
        self.operations.append({"gate": "CZ", "target": [control, target]})
        return self

    def cy(self, control: int, target: int) -> 'QuantumCircuit':
        """CY gate — controlled-Y."""
        self._val2(control, target)
        self.operations.append({"gate": "CY", "target": [control, target]})
        return self

    def ch(self, control: int, target: int) -> 'QuantumCircuit':
        """CH gate — controlled-Hadamard."""
        self._val2(control, target)
        self.operations.append({"gate": "CH", "target": [control, target]})
        return self

    # ─────────────────────────────────────────────────────────────
    #  TWO-QUBIT GATES — SWAP
    # ─────────────────────────────────────────────────────────────

    def swap(self, qubit_a: int, qubit_b: int) -> 'QuantumCircuit':
        """SWAP gate — swaps the state of two qubits."""
        self._val2(qubit_a, qubit_b)
        self.operations.append({"gate": "SWAP", "target": [qubit_a, qubit_b]})
        return self

    def iswap(self, qubit_a: int, qubit_b: int) -> 'QuantumCircuit':
        """iSWAP gate — SWAP with imaginary phase factor."""
        self._val2(qubit_a, qubit_b)
        self.operations.append({"gate": "iSWAP", "target": [qubit_a, qubit_b]})
        return self

    # ─────────────────────────────────────────────────────────────
    #  TWO-QUBIT GATES — CONTROLLED ROTATION
    # ─────────────────────────────────────────────────────────────

    def crx(self, control: int, target: int, theta: float) -> 'QuantumCircuit':
        """CRX(θ) — controlled-RX rotation."""
        self._val2(control, target)
        self.operations.append({"gate": "CRX", "target": [control, target], "param": float(theta)})
        return self

    def cry(self, control: int, target: int, theta: float) -> 'QuantumCircuit':
        """CRY(θ) — controlled-RY rotation."""
        self._val2(control, target)
        self.operations.append({"gate": "CRY", "target": [control, target], "param": float(theta)})
        return self

    def crz(self, control: int, target: int, theta: float) -> 'QuantumCircuit':
        """CRZ(θ) — controlled-RZ rotation. Key gate for QFT circuits."""
        self._val2(control, target)
        self.operations.append({"gate": "CRZ", "target": [control, target], "param": float(theta)})
        return self

    def cp(self, control: int, target: int, lam: float) -> 'QuantumCircuit':
        """CP(λ) — controlled phase gate. IBM Qiskit equivalent of CRZ."""
        self._val2(control, target)
        self.operations.append({"gate": "CP", "target": [control, target], "param": float(lam)})
        return self

    # ─────────────────────────────────────────────────────────────
    #  THREE-QUBIT GATES
    # ─────────────────────────────────────────────────────────────

    def ccx(self, control1: int, control2: int, target: int) -> 'QuantumCircuit':
        """CCX / Toffoli gate — doubly-controlled NOT."""
        self._val3(control1, control2, target)
        self.operations.append({"gate": "Toffoli", "target": [control1, control2, target]})
        return self

    def toffoli(self, control1: int, control2: int, target: int) -> 'QuantumCircuit':
        """Alias for CCX (Toffoli gate)."""
        return self.ccx(control1, control2, target)

    def cswap(self, control: int, target1: int, target2: int) -> 'QuantumCircuit':
        """CSWAP / Fredkin gate — controlled-SWAP."""
        self._val3(control, target1, target2)
        self.operations.append({"gate": "Fredkin", "target": [control, target1, target2]})
        return self

    def fredkin(self, control: int, target1: int, target2: int) -> 'QuantumCircuit':
        """Alias for CSWAP (Fredkin gate)."""
        return self.cswap(control, target1, target2)

    def rccx(self, control1: int, control2: int, target: int) -> 'QuantumCircuit':
        """RCCX (Margolus gate) — relative-phase Toffoli for circuit optimization."""
        self._val3(control1, control2, target)
        self.operations.append({"gate": "RCCX", "target": [control1, control2, target]})
        return self

    # ─────────────────────────────────────────────────────────────
    #  MEASUREMENT
    # ─────────────────────────────────────────────────────────────

    def measure(self, target: int) -> 'QuantumCircuit':
        """Measure a specific qubit. AnuAstra collapses the full state globally."""
        self._validate_qubit(target)
        return self

    def measure_all(self) -> 'QuantumCircuit':
        """Measure all allocated qubits. (Qiskit alias — AnuAstra auto-collapses.)"""
        return self
