"""Batch processing module.

Provides a convenience wrapper that runs the full
:class:`~lasertram.tram.LaserTRAM` workflow for a single spot.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import pandas as pd
    from lasertram.tram.tram import LaserTRAM


# TODO:
# - [ ] fix: error thrown in process spot if despike is true. must normalize data first
def process_spot(
    spot: LaserTRAM,
    raw_data: pd.DataFrame,
    bkgd: tuple[float, float],
    keep: tuple[float, float],
    int_std: str,
    omit: tuple[float, float] | None = None,
    despike: bool = False,
    output_report: bool = True,
    verbose: bool = False,
) -> None:
    """Run the full LaserTRAM processing workflow for one spot.

    This is a convenience function that calls each
    :class:`~lasertram.tram.LaserTRAM` method in sequence.

    Parameters
    ----------
    spot : LaserTRAM
        An initialised :class:`~lasertram.tram.LaserTRAM` object.
    raw_data : pandas.DataFrame
        Raw counts-per-second data for the spot.
    bkgd : tuple of float
        ``(start, stop)`` analysis times for the background interval.
    keep : tuple of float
        ``(start, stop)`` analysis times for the signal interval.
    int_std : str
        Internal standard analyte column name, e.g. ``"29Si"``.
    omit : tuple of float or None, optional
        ``(start, stop)`` analysis times to omit from the signal
        interval, by default ``None``.
    despike : bool, optional
        Whether to despike all analytes, by default ``False``.
    output_report : bool, optional
        Whether to generate a one-row output report, by default
        ``True``.
    verbose : bool, optional
        Whether to print status messages, by default ``False``.

    Examples
    --------
    >>> from lasertram import LaserTRAM, helpers
    >>> spot = LaserTRAM(name="GSD-1G_-_1")
    >>> helpers.batch.process_spot(
    ...     spot,
    ...     raw_data=raw_data.loc["GSD-1G_-_1", :],
    ...     bkgd=(0, 20000),
    ...     keep=(20000, 60000),
    ...     int_std="29Si",
    ... )
    """
    # assign data to the spot
    spot.get_data(raw_data, verbose=verbose)

    # assign the internal standard analyte
    spot.assign_int_std(int_std)
    # assign intervals for background and ablation signal
    spot.assign_intervals(bkgd=bkgd, keep=keep, omit=omit)
    # assign and save the median background values
    spot.get_bkgd_data()
    # remove the median background values from the ablation interval
    spot.subtract_bkgd()
    # calculate detection limits based off background values
    spot.get_detection_limits()
    # normalize the ablation interval to the internal standard analyte,
    # get the median values, and the standard error
    spot.normalize_interval()
    # despike the data if desired
    if despike is True:
        spot.despike_data(analyte_list="all")

    if output_report is True:
        spot.make_output_report()
