# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2025 Scipp contributors (https://github.com/scipp)
from collections.abc import Mapping

import scipp as sc
from scippneutron.chopper import DiskChopper

from .types import SimulationResults


def simulate_beamline(
    choppers: Mapping[str, DiskChopper],
    neutrons: int = 1_000_000,
    seed: int | None = None,
    facility: str = 'ess',
) -> SimulationResults:
    """
    Simulate a pulse of neutrons propagating through a chopper cascade using the
    ``tof`` package (https://tof.readthedocs.io).

    Parameters
    ----------
    choppers:
        A dict of DiskChopper objects representing the choppers in the beamline. See
        https://scipp.github.io/scippneutron/user-guide/chopper/processing-nexus-choppers.html#Build-DiskChopper
        for more information.
    neutrons:
        Number of neutrons to simulate.
    seed:
        Seed for the random number generator used in the simulation.
    facility:
        Facility where the experiment is performed.
    """
    import tof

    tof_choppers = [
        tof.Chopper(
            frequency=abs(ch.frequency),
            direction=tof.AntiClockwise
            if (ch.frequency.value > 0.0)
            else tof.Clockwise,
            open=ch.slit_begin,
            close=ch.slit_end,
            phase=abs(ch.phase),
            distance=ch.axle_position.fields.z,
            name=name,
        )
        for name, ch in choppers.items()
    ]
    source = tof.Source(facility=facility, neutrons=neutrons, seed=seed)
    if not tof_choppers:
        events = source.data.squeeze()
        return SimulationResults(
            time_of_arrival=events.coords["time"],
            speed=events.coords["speed"],
            wavelength=events.coords["wavelength"],
            weight=events.data,
            distance=0.0 * sc.units.m,
        )
    model = tof.Model(source=source, choppers=tof_choppers)
    results = model.run()
    # Find name of the furthest chopper in tof_choppers
    furthest_chopper = max(tof_choppers, key=lambda c: c.distance)
    events = results[furthest_chopper.name].data.squeeze()
    events = events[
        ~(events.masks["blocked_by_others"] | events.masks["blocked_by_me"])
    ]
    return SimulationResults(
        time_of_arrival=events.coords["toa"],
        speed=events.coords["speed"],
        wavelength=events.coords["wavelength"],
        weight=events.data,
        distance=furthest_chopper.distance,
    )
