
import numpy as np

# ─────────────────────────────────────────────────────────────────
#  Qiskit Compatibility Layer
#
#  to_qasm(circuit)          → export as OpenQASM 2.0 string
#  from_qasm(qasm_string)    → import OpenQASM into PKTron circuit
#  to_qiskit(circuit)        → convert to real Qiskit circuit
#  from_qiskit(qk_circuit)   → convert Qiskit circuit to PKTron
# ─────────────────────────────────────────────────────────────────

def to_qasm(circuit):
    """
    Export a PKTron circuit to OpenQASM 2.0 format.

    Usage:
        qasm = to_qasm(qc)
        print(qasm)
    """
    lines = []
    lines.append("OPENQASM 2.0;")
    lines.append('include "qelib1.inc";')
    lines.append(f"qreg q[{circuit.num_qubits}];")
    lines.append(f"creg c[{circuit.num_qubits}];")

    for entry in circuit.gate_history:
        if isinstance(entry, tuple):
            gate, qubits, params = entry
        else:
            # parse string format
            parts = entry.split()
            gate = parts[0]
            nums = []
            for p in parts[1:]:
                try: nums.append(float(p.strip("(),")))
                except ValueError: pass
            qubits = [int(x) for x in nums[-min(3,len(nums)):]]
            params = nums[:-min(3,len(nums))] if len(nums) > 1 else []

        gate_map = {
            "H":"h","X":"x","Y":"y","Z":"z",
            "S":"s","Sdg":"sdg","T":"t","Tdg":"tdg",
            "CNOT":"cx","CZ":"cz","SWAP":"swap",
        }

        g = gate_map.get(gate, gate.lower())

        if gate in ("Rx","Ry","Rz") and params:
            lines.append(f"{g}({params[0]:.6f}) q[{qubits[0]}];")
        elif gate == "U3" and len(params) >= 3:
            lines.append(f"u3({params[0]:.6f},{params[1]:.6f},{params[2]:.6f}) q[{qubits[0]}];")
        elif gate == "Toffoli" and len(qubits) == 3:
            lines.append(f"ccx q[{qubits[0]}],q[{qubits[1]}],q[{qubits[2]}];")
        elif gate in ("CNOT","CZ","SWAP") and len(qubits) == 2:
            lines.append(f"{g} q[{qubits[0]}],q[{qubits[1]}];")
        elif qubits:
            lines.append(f"{g} q[{qubits[0]}];")

    return "\n".join(lines)


def from_qasm(qasm_string):
    """
    Import an OpenQASM 2.0 string into a PKTron QuantumCircuit.

    Usage:
        qc = from_qasm(qasm_string)
    """
    from .quantum import QuantumCircuit
    import re

    num_qubits = 2   # default
    for line in qasm_string.splitlines():
        m = re.search(r"qreg\s+\w+\[(\d+)\]", line)
        if m:
            num_qubits = int(m.group(1))
            break

    qc = QuantumCircuit(num_qubits)

    for line in qasm_string.splitlines():
        line = line.strip().rstrip(";")
        if not line or line.startswith("//") or line.startswith("OPENQASM")                 or line.startswith("include") or line.startswith("qreg")                 or line.startswith("creg") or line.startswith("measure"):
            continue

        # parameterized gate: gate(params) q[i]
        m = re.match(r"(\w+)\(([^)]+)\)\s+(.*)", line)
        if m:
            g, raw_params, raw_qubits = m.group(1), m.group(2), m.group(3)
            params  = [float(x.strip()) for x in raw_params.split(",")]
            qubits  = [int(x) for x in re.findall(r"\d+", raw_qubits)]
            if g in ("rx","ry","rz") and qubits:
                getattr(qc, g)(qubits[0], params[0])
            elif g == "u3" and len(params) >= 3 and qubits:
                qc.gate_history.append(f"U3 {params[0]:.4f} {params[1]:.4f} {params[2]:.4f} on qubit {qubits[0]}")
            continue

        # plain gate: gate q[i] or gate q[i],q[j]
        parts  = line.split()
        g      = parts[0] if parts else ""
        qubits = [int(x) for x in re.findall(r"\d+", " ".join(parts[1:]))]

        gate_map = {"h":"h","x":"x","y":"y","z":"z","s":"s","sdg":"sdg",
                    "t":"t","tdg":"tdg","cx":"cnot","cz":"cz","swap":"swap",
                    "ccx":"toffoli"}
        method = gate_map.get(g)
        if method and qubits:
            if method in ("cnot","cz","swap") and len(qubits) >= 2:
                getattr(qc, method)(qubits[0], qubits[1])
            elif method == "toffoli" and len(qubits) >= 3:
                qc.toffoli(qubits[0], qubits[1], qubits[2])
            else:
                getattr(qc, method)(qubits[0])

    return qc


def to_qiskit(circuit):
    """
    Convert a PKTron circuit to a real Qiskit QuantumCircuit.
    Requires qiskit to be installed.

    Usage:
        from pktron.qiskit_compat_v2 import to_qiskit
        qk_circuit = to_qiskit(pktron_circuit)
    """
    try:
        from qiskit import QuantumCircuit as QkCircuit
    except ImportError:
        raise ImportError("Install qiskit first: pip install qiskit")

    qk = QkCircuit(circuit.num_qubits, circuit.num_qubits)

    for entry in circuit.gate_history:
        if isinstance(entry, tuple):
            gate, qubits, params = entry
        else:
            parts = entry.split()
            gate = parts[0]
            nums = []
            for p in parts[1:]:
                try: nums.append(float(p.strip("(),")))
                except ValueError: pass
            qubits = [int(x) for x in nums[-min(3,len(nums)):]]
            params = nums[:-len(qubits)] if len(nums) > len(qubits) else []

        try:
            if gate == "H"    and qubits: qk.h(qubits[0])
            elif gate == "X"  and qubits: qk.x(qubits[0])
            elif gate == "Y"  and qubits: qk.y(qubits[0])
            elif gate == "Z"  and qubits: qk.z(qubits[0])
            elif gate == "S"  and qubits: qk.s(qubits[0])
            elif gate == "T"  and qubits: qk.t(qubits[0])
            elif gate == "Sdg"and qubits: qk.sdg(qubits[0])
            elif gate == "Tdg"and qubits: qk.tdg(qubits[0])
            elif gate == "Rx" and qubits and params: qk.rx(params[0], qubits[0])
            elif gate == "Ry" and qubits and params: qk.ry(params[0], qubits[0])
            elif gate == "Rz" and qubits and params: qk.rz(params[0], qubits[0])
            elif gate == "CNOT" and len(qubits)>=2:  qk.cx(qubits[0], qubits[1])
            elif gate == "CZ"   and len(qubits)>=2:  qk.cz(qubits[0], qubits[1])
            elif gate == "SWAP" and len(qubits)>=2:  qk.swap(qubits[0], qubits[1])
            elif gate == "Toffoli" and len(qubits)>=3: qk.ccx(qubits[0],qubits[1],qubits[2])
        except Exception:
            pass

    return qk


def from_qiskit(qk_circuit):
    """
    Convert a Qiskit QuantumCircuit to a PKTron QuantumCircuit.

    Usage:
        from pktron.qiskit_compat_v2 import from_qiskit
        pktron_qc = from_qiskit(qiskit_circuit)
    """
    from .quantum import QuantumCircuit
    qc = QuantumCircuit(qk_circuit.num_qubits)

    for instr in qk_circuit.data:
        gate = instr.operation.name.lower()
        qubits = [qk_circuit.find_bit(q).index for q in instr.qubits]
        params = [float(p) for p in instr.operation.params]

        try:
            if gate == "h":    qc.h(qubits[0])
            elif gate == "x":  qc.x(qubits[0])
            elif gate == "y":  qc.y(qubits[0])
            elif gate == "z":  qc.z(qubits[0])
            elif gate == "s":  qc.s(qubits[0])
            elif gate == "t":  qc.t(qubits[0])
            elif gate == "sdg":qc.sdg(qubits[0])
            elif gate == "tdg":qc.tdg(qubits[0])
            elif gate == "rx" and params:  qc.rx(qubits[0], params[0])
            elif gate == "ry" and params:  qc.ry(qubits[0], params[0])
            elif gate == "rz" and params:  qc.rz(qubits[0], params[0])
            elif gate == "cx"  and len(qubits)>=2: qc.cnot(qubits[0], qubits[1])
            elif gate == "cz"  and len(qubits)>=2: qc.cz(qubits[0], qubits[1])
            elif gate == "swap"and len(qubits)>=2: qc.swap(qubits[0], qubits[1])
            elif gate == "ccx" and len(qubits)>=3: qc.toffoli(qubits[0],qubits[1],qubits[2])
        except Exception:
            pass

    return qc
