"""Bridge between ``config.toml`` and the rest of the GUI.

Loads ``config.toml`` (located next to this file) at import time and
exposes helpers for:

* **File extensions** — ``sorted_extension_list()``
* **Processing stages** — name / object lookups for epocher, scorer, long
* **Presets** — iteration, component extraction, and default output flags

Algorithm classes are imported at module level so that ``globals()`` can
resolve the class name strings stored in ``config.toml``.
"""

import os
import tomllib


# Algorithm classes must be importable so globals() can resolve them by name.
from ada.preprocessing.epocher import ActivityIndex, Downsampler, MIMS, MVM
from ada.short.cole_kripke import ColeKripke, Sazonov, Scripps, Webster, Ucsd
from ada.short.unified import UFA
from ada.data_containers.scored import ScoredShort
from ada.long.cosinor import Linear, Nonlinear, Hill, Antilogistic, Arctangent
from ada.long.nonparametric import DFA, SimpleNonparametric
from ada.long.ar import Spectrum


# Human-readable labels for each pipeline stage (used in status messages)
STAGE_TO_NAME = {
    'epocher': "Collapsing into epochs",
    'scorer':  "Sleep/wake scoring",
    'long':    "Circadian rhythm analysis",
}

# Loaded once at import time; shared by all helpers below
with open(os.path.join(os.path.dirname(__file__), "config.toml"), "rb") as f:
    CONFIG = tomllib.load(f)


# ---------------------------------------------------------------------------
# Extension helpers
# ---------------------------------------------------------------------------

def sorted_extension_list():
    """Return the configured file extensions in sorted order."""
    return sorted(CONFIG['extensions'])


# ---------------------------------------------------------------------------
# Stage lookup helpers (epocher / scorer / long)
# ---------------------------------------------------------------------------

def _stage_names(stage):
    """Return the display names of all algorithms for *stage*."""
    return [entry['name'] for entry in CONFIG[stage].values()]


def _stage_object(stage, display_name):
    """Return the algorithm class for *display_name* under *stage*."""
    matches = [entry['algorithm'] for entry in CONFIG[stage].values()
               if entry['name'] == display_name]
    assert len(matches) == 1
    return globals()[matches[0]]


def epocher_function_names():
    """Display names for all epoching algorithms."""
    return _stage_names('epocher')

def epocher_object(name):
    """Algorithm class for the epocher with display *name*."""
    return _stage_object('epocher', name)

def scorer_function_names():
    """Display names for all sleep/wake scoring algorithms."""
    return _stage_names('scorer')

def scorer_object(name):
    """Algorithm class for the scorer with display *name*."""
    return _stage_object('scorer', name)

def long_function_names():
    """Display names for all circadian rhythm algorithms."""
    return _stage_names('long')

def long_object(name):
    """Algorithm class for the long-term method with display *name*."""
    return _stage_object('long', name)


def object_description(stage, display_name):
    """Return a human-readable description for a stage algorithm."""
    entry = next(e for e in CONFIG[stage].values() if e['name'] == display_name)
    return f"{STAGE_TO_NAME[stage]}: {entry.get('desc', display_name)}"


# ---------------------------------------------------------------------------
# Preset helpers
# ---------------------------------------------------------------------------

def preset_function_names():
    """Yield ``(button_label, config_key)`` pairs for all configured presets."""
    for key, preset in CONFIG['presets'].items():
        if preset['abbreviation']:
            yield key.replace('_', ' ').upper(), key
        else:
            yield key.replace('_', ' ').title(), key


def _extract_stage(preset, stage):
    """Extract (algorithm_name, settings_dict) for an optional preset stage.

    Returns ``(None, None)`` when the preset does not include this stage.
    A threshold of ``0`` in the TOML file is treated as ``None`` (auto).
    """
    if stage not in preset:
        return None, None
    section = preset[stage]
    settings = {k: v for k, v in section.items() if k != 'name'}
    if settings.get('threshold') == 0:
        settings['threshold'] = None
    return section['name'], settings


def preset_components(preset_name):
    """Extract algorithm names and parameter overrides from a preset."""
    preset = CONFIG['presets'][preset_name]
    result = {}

    result['epocher'] = preset['epocher']['name']
    result['epocher_settings'] = {k: v for k, v in preset['epocher'].items()
                                  if k != 'name'}

    result['scorer'], result['scorer_settings'] = _extract_stage(preset, 'scorer')
    result['long'],   result['long_settings']   = _extract_stage(preset, 'long')

    return result


def preset_save_defaults(preset_name):
    """Return default output-format flags for a preset."""
    return {
        'ada':     {'epocher': False, 'scorer': False, 'long': False},
        'csv':     {'epocher': False, 'scorer': False, 'long': False},
        'plot':    {'epocher': False, 'scorer': True,  'long': True, 'profile': False},
        'summary': {'scorer': True, 'long': True},
    }
