"""Validation of CORSIKA data and log files."""

import logging

from simtools.sim_events import file_info
from simtools.testing.log_inspector import check_plain_logs
from simtools.utils import general

_logger = logging.getLogger(__name__)


def validate_corsika_output(data_files, log_files, expected_shower_events=None, curved_atmo=False):
    """
    Validate CORSIKA output files.

    Parameters
    ----------
    data_files: list of Path
        List of paths to CORSIKA data files.
    log_files: list of Path
        List of paths to CORSIKA log files.
    expected_shower_events: int, optional
        Expected number of shower events.
    curved_atmo: bool, optional
        Whether the CORSIKA simulation was run with the curved atmosphere option.
    """
    data_files = general.ensure_iterable(data_files) if data_files is not None else []
    log_files = general.ensure_iterable(log_files)

    validate_event_numbers(data_files, expected_shower_events)
    validate_log_files(
        log_files, expected_shower_events=expected_shower_events, curved_atmo=curved_atmo
    )


def validate_event_numbers(data_files, expected_shower_events, tolerance=1.0e-3):
    """
    Validate the number of simulated events in CORSIKA output files.

    Allow for a small mismatch in the number of requested events.

    Parameters
    ----------
    expected_shower_events: int
        Expected number of simulated shower events.

    Raises
    ------
    ValueError
        If the number of simulated events does not match the expected number.
    """

    def consistent(a, b, tol):
        return abs(a - b) / max(a, b) <= tol

    event_errors = []

    for file in data_files:
        shower_events, _ = file_info.get_simulated_events(file)

        if shower_events != expected_shower_events:
            if consistent(shower_events, expected_shower_events, tol=tolerance):
                _logger.warning(
                    f"Small mismatch in number of events in: {file}: "
                    f"shower events: {shower_events} (expected: {expected_shower_events})"
                )
            else:
                event_errors.append(
                    f"Number of simulated shower events ({shower_events}) does not match "
                    f"the expected number ({expected_shower_events}) in CORSIKA {file}."
                )
        else:
            _logger.info(
                f"Consistent number of events in: {file}: shower events: {shower_events}"
                f" (expected: {expected_shower_events})"
            )

    if event_errors:
        _logger.error("Inconsistent event counts found in CORSIKA output:")
        for error in event_errors:
            _logger.error(f" - {error}")
        error_message = "Inconsistent event counts found in CORSIKA output:\n" + "\n".join(
            f" - {error}" for error in event_errors
        )
        raise ValueError(error_message)


def validate_log_files(log_files, expected_shower_events=None, curved_atmo=False):
    """
    Validate CORSIKA log files.

    Parameters
    ----------
    log_files: list of Path
        List of paths to CORSIKA log files.
    expected_shower_events: int, optional
        Expected number of shower events.
    curved_atmo: bool, optional
        Whether the CORSIKA simulation was run with the curved atmosphere option.

    Raises
    ------
    ValueError
        If log files do not contain expected patterns indicating successful completion.
    """
    patterns = ["========== END OF RUN ======="]
    forbidden = []

    if expected_shower_events is not None:
        patterns.append(f"NUMBER OF GENERATED EVENTS =          {expected_shower_events}")

    if curved_atmo:
        patterns.append("CURVED VERSION WITH SLIDING PLANAR ATMOSPHERE")
    else:
        forbidden.append("CORSIKA was compiled without CURVED option.")

    ok = check_plain_logs(
        log_files,
        {
            "pattern": patterns,
            "forbidden_pattern": forbidden,
        },
    )

    if not ok:
        raise ValueError(
            f"Log files {log_files} do not contain expected patterns "
            "indicating successful completion."
        )
