#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 10 10:01:55 2023

@author: dan
"""


class ModSpec:
    """Model specification defined by a set of named feature flags.

    Supports building a specification incrementally by starting from an
    existing ``ModSpec`` and adding or removing features. Features are stored
    sorted and deduplicated so that the resulting label is deterministic.

    Parameters
    ----------
    mod : ModSpec, optional
        A base ``ModSpec`` whose features are inherited. Features in
        ``remove_list`` are excluded from the inherited set.
    add_list : list of str, optional
        Additional feature names to include. Defaults to ``[]``.
    remove_list : list of str, optional
        Feature names to exclude when inheriting from ``mod``.
        Defaults to ``[]``.
    label : str, optional
        Human-readable label for this specification. If not provided, the
        label is generated by joining the sorted feature names with ``'_'``.

    Attributes
    ----------
    features : list of str
        Sorted, deduplicated list of active feature names.
    label : str
        String label identifying the specification.

    Examples
    --------
    >>> base = ModSpec(add_list=['fe', 'controls'])
    >>> extended = ModSpec(mod=base, add_list=['interactions'])
    >>> 'fe' in extended
    True
    >>> str(extended)
    'controls_fe_interactions'
    """

    def __init__(self, mod=None, add_list=None, remove_list=None, label=None):
        """Initialise a ModSpec.

        Parameters
        ----------
        mod : ModSpec, optional
            Base specification to inherit features from.
        add_list : list of str, optional
            Features to add. Defaults to ``[]``.
        remove_list : list of str, optional
            Features to exclude when inheriting from ``mod``.
            Defaults to ``[]``.
        label : str, optional
            Override the auto-generated label.
        """
        if add_list is None:
            add_list = []
        if remove_list is None:
            remove_list = []

        self.features = add_list.copy()
        if mod is not None:
            self.features += [
                feature for feature in mod.features if feature not in remove_list
            ]

        self.features = sorted(list(set(self.features)))

        if label is None:
            self.label = "_".join(self.features)

    def __contains__(self, key):
        """Return ``True`` if *key* is an active feature in this spec.

        Parameters
        ----------
        key : str
            Feature name to test.

        Returns
        -------
        bool
            ``True`` if *key* is present in :attr:`features`.
        """
        return key in self.features

    def __str__(self):
        """Return the string label of this specification.

        Returns
        -------
        str
            The :attr:`label` attribute.
        """
        return self.label
