"""Circuit-level gate fusion helpers."""

from __future__ import annotations

import numpy as np


def fuse_qiskit_one_qubit_gates(circuit):
    """Fuse consecutive unconditional one-qubit gates in a Qiskit circuit.

    The fused gate is emitted as a Qiskit ``UnitaryGate``.  qmatchatea's Qiskit
    executor applies generic gates through ``operation.to_matrix()``, so this
    reduces Python gate dispatch without changing the physical circuit.
    """

    from qiskit import QuantumCircuit
    from qiskit.circuit.library import UnitaryGate

    fused = QuantumCircuit(*circuit.qregs, *circuit.cregs, name=circuit.name)
    pending = {}

    def qubit_index(qubit):
        return circuit.find_bit(qubit).index

    def flush_qubit(index):
        matrix = pending.pop(index, None)
        if matrix is not None:
            fused.append(UnitaryGate(matrix, label="fused1q"), [fused.qubits[index]])

    def flush_all():
        for index in sorted(tuple(pending)):
            flush_qubit(index)

    for instruction in circuit.data:
        operation = instruction.operation
        qubits = instruction.qubits
        clbits = instruction.clbits
        condition = getattr(operation, "condition", None)

        can_fuse = (
            len(qubits) == 1
            and len(clbits) == 0
            and condition is None
            and hasattr(operation, "to_matrix")
        )
        if can_fuse:
            try:
                matrix = np.asarray(operation.to_matrix(), dtype=np.complex128)
            except Exception:
                can_fuse = False

        if can_fuse:
            index = qubit_index(qubits[0])
            if index in pending:
                pending[index] = matrix @ pending[index]
            else:
                pending[index] = matrix
            continue

        touched = {qubit_index(qubit) for qubit in qubits}
        for index in sorted(touched):
            flush_qubit(index)
        if len(qubits) != 1:
            flush_all()
        fused.append(operation, [fused.qubits[qubit_index(q)] for q in qubits], clbits)

    flush_all()
    return fused
