"""String parsing tools."""
import re


def get_default_archetype_prefix():
    """Return standard string prefix for archetype classes, to be replaced with domain strings."""
    return "ARCH"


def get_subcomponent_indicator():
    """Return string that indicates a model is meant to be a subcomponent."""
    return "subcomponent"


def get_design_indicator():
    """Return the indicator for a component design.

    This tells us which sub-dictionary in a model catalog contains the component designs.
    """
    return "design"


def get_model_input_suffix(model_type: str):
    """Return the full suffix for a specific mixin type from an input dictionary."""
    return "_" + model_type + "_model"


def get_model_input_key(component_name: str, model_type: str) -> str:
    """Return the flat key for a component's model type, combining component name and model type.

    For example:
        get_model_key('component1', 'design') -> 'component1_design_model'
        get_model_key('component1', 'cost')   -> 'component1_cost_model'
    """
    return component_name + get_model_input_suffix(model_type)


def get_component_design_key(component_name: str) -> str:
    """Return the input dictionary key that identifies the design model for a component.

    For example:
        get_component_design_key('pvpanel') -> 'pvpanel_design_model'
    """
    return get_model_input_key(component_name, get_design_indicator())


def is_mixin_input_key(key: str, component_name: str) -> bool:
    """Return True if key is a mixin (non-design) model input key for the given component.

    A mixin input key has the form '{component_name}_{model_type}_model' where
    model_type is anything other than the design indicator.

    For example:
        is_mixin_input_key('pvpanel_cost_model', 'pvpanel')   -> True
        is_mixin_input_key('pvpanel_design_model', 'pvpanel') -> False
        is_mixin_input_key('other_cost_model', 'pvpanel')     -> False
    """
    prefix = component_name + "_"
    if not (key.startswith(prefix) and key.endswith("_model")):
        return False
    return key != get_component_design_key(component_name)


def get_mixin_inputs(component_name: str, data: dict) -> dict:
    """Return the subset of data whose keys are mixin input keys for the given component.

    Scans the input dictionary for any entries of the form '{component_name}_{model_type}_model'
    where model_type is not the design indicator, and returns them as a dict.

    For example, given component_name='pvpanel' and data containing:
        {'pvpanel_design_model': 'SimpleFixed', 'pvpanel_cost_model': 'SimpleCost', 'other': 1}

    Returns:
        {'pvpanel_cost_model': 'SimpleCost'}

    """
    return {k: v for k, v in data.items() if is_mixin_input_key(k, component_name)}


def get_override_name(field_name):
    """Return the override input name associated with a certain model field."""
    return f"override_{field_name}"


def plot_param_name(param_name):
    """Convert parameter names into more readable plotting names."""
    def insert_caret(text):
        """Insert carets where we find two numbers separated by underscores in the parameter name.

        For example:
        'my_parameter__m2' -> my parameter (m^2)
        'my_other_parameter__m2_3' -> my other parameter (m^(2/3))
        """
        # Step 1: Handle letter followed by "num1_num2" -> "^(num1/num2)"
        text = re.sub(r'([a-zA-Z])(\d+) (\d+)(?=\s|$)', r'\1^(\2/\3)', text)

        # Step 2: Handle normal "letter+number" cases (if not already modified)
        text = re.sub(r'([a-zA-Z])(\d+)(?=\s|$)', r'\1^\2', text)

        return text.strip()  # Ensure no extra spaces

    parts = param_name.split("__", maxsplit=2)
    parts = tuple(part.replace("_", " ") for part in parts)
    outstring = parts[0]
    if len(parts) > 1:
        outstring += ' ('+insert_caret(parts[1])
        if len(parts) == 3:
            outstring += ' / '+insert_caret(parts[2])
        outstring += ')'
    return outstring
