from __future__ import annotations

from dataclasses import dataclass

from ..backends.base import Backend
from ..errors import ValidationError
from .basis import basis_map
from .opt1q import optimize_1q
from .routing import route_on_coupling
from .validate import validate


@dataclass(frozen=True)
class PassConfig:
    do_routing: bool = True
    do_opt1q: bool = True
    eps: float = 1e-9  # for small-angle pruning in opt1q


def transpile(
    circuit,
    backend: Backend,
    config: PassConfig | None = None,
    return_report: bool = False,
):
    """
    Run the standard pipeline:
      1) basis_map  -> rewrite non-native gates (e.g., h) to {x,sx,rz,cx,measure}
      2) validate   -> structure + basis checks; compute 'needs_routing'
      3) route      -> (optional) insert SWAP chains so CXs follow coupling_map
      4) optimize_1q-> (optional) peephole: merge RZ; SX·SX→X; X·X cancels

    Returns either the mapped circuit, or (mapped circuit, ValidationReport) if return_report=True.
    """
    cfg = config or PassConfig()

    # 1) map to native
    mapped = basis_map(circuit, backend)

    # 2) validate native circuit
    report = validate(mapped, backend)

    # 3) routing if required and enabled
    routed = mapped
    if cfg.do_routing and report.needs_routing:
        routed = route_on_coupling(mapped, backend)
        # Re-validate after routing; should not need routing now
        report = validate(routed, backend)
        if report.needs_routing:
            raise ValidationError("Routing pass did not satisfy coupling constraints.")

    # 4) 1q optimizations
    optimized = optimize_1q(routed, eps=cfg.eps) if cfg.do_opt1q else routed

    if return_report:
        return optimized, report
    return optimized
