"""Compile Control configurations into Kibana view models."""

from collections.abc import Sequence

from kb_dashboard_core.controls import ControlTypes
from kb_dashboard_core.controls.config import (
    ControlSettings,
    ESQLFieldControl,
    ESQLFunctionControl,
    ESQLQueryMultiSelectControl,
    ESQLQuerySingleSelectControl,
    ESQLStaticMultiSelectControl,
    ESQLStaticSingleSelectControl,
    MatchTechnique,
    OptionsListControl,
    RangeSliderControl,
    TimeSliderControl,
)
from kb_dashboard_core.controls.types import EsqlControlType
from kb_dashboard_core.controls.view import (
    KBN_DEFAULT_CONTROL_WIDTH,
    ChainingSystemEnum,
    ControlStyleEnum,
    KbnControlGroupInput,
    KbnControlPanelsJson,
    KbnControlSort,
    KbnControlTypes,
    KbnESQLControl,
    KbnESQLControlExplicitInput,
    KbnIgnoreParentSettingsJson,
    KbnOptionsListControl,
    KbnOptionsListControlExplicitInput,
    KbnRangeSliderControl,
    KbnRangeSliderControlExplicitInput,
    KbnTimeSliderControl,
    KbnTimeSliderControlExplicitInput,
    SearchTechnique,
)
from kb_dashboard_core.shared.compile import return_if, return_if_equals
from kb_dashboard_core.shared.defaults import default_false, default_if_none
from kb_dashboard_core.shared.logging import log_compile
from kb_dashboard_core.shared.view import KbnReference


@log_compile
def compile_options_list_control(order: int, *, control: OptionsListControl) -> tuple[KbnOptionsListControl, KbnReference]:
    """Compile an OptionsListControl into its Kibana view model representation.

    Args:
        order (int): The order of the control in the dashboard.
        control (OptionsListControl): The OptionsListControl object to compile.

    Returns:
        tuple: A tuple containing the compiled control and its data view reference.

    """
    match_technique_to_search_technique: dict[MatchTechnique | None, SearchTechnique] = {
        MatchTechnique.PREFIX: SearchTechnique.PREFIX,
        MatchTechnique.CONTAINS: SearchTechnique.WILDCARD,
        MatchTechnique.EXACT: SearchTechnique.EXACT,
    }

    # Determine singleSelect value from multiple field
    single_select_value: bool | None = None
    if control.multiple is not None:
        single_select_value = not control.multiple

    control_id = control.get_id()

    kbn_control = KbnOptionsListControl(
        grow=default_false(control.fill_width),
        order=order,
        width=default_if_none(control.width, KBN_DEFAULT_CONTROL_WIDTH),
        explicitInput=KbnOptionsListControlExplicitInput(
            id=control_id,
            dataViewId=control.data_view,
            fieldName=control.field,
            title=control.label,
            runPastTimeout=return_if(var=control.wait_for_results, is_true=True, is_false=False, default=None),
            singleSelect=return_if(var=single_select_value, is_true=True, is_false=False, default=None),
            searchTechnique=match_technique_to_search_technique.get(control.match_technique, SearchTechnique.PREFIX),
            selectedOptions=[],
            sort=KbnControlSort(by='_count', direction='desc'),
        ),
    )

    reference = KbnReference(
        type='index-pattern',
        id=control.data_view,
        name=f'controlGroup_{control_id}:optionsListDataView',
    )

    return kbn_control, reference


@log_compile
def compile_range_slider_control(order: int, *, control: RangeSliderControl) -> tuple[KbnRangeSliderControl, KbnReference]:
    """Compile a RangeSliderControl into its Kibana view model representation.

    Args:
        order (int): The order of the control in the dashboard.
        control (RangeSliderControl): The RangeSliderControl object to compile.

    Returns:
        tuple: A tuple containing the compiled control and its data view reference.

    """
    control_id = control.get_id()

    kbn_control = KbnRangeSliderControl(
        grow=default_false(control.fill_width),
        order=order,
        width=default_if_none(control.width, 'medium'),
        explicitInput=KbnRangeSliderControlExplicitInput(
            id=control_id,
            dataViewId=control.data_view,
            fieldName=control.field,
            step=default_if_none(control.step, 1),
            title=control.label,
        ),
    )

    reference = KbnReference(
        type='index-pattern',
        id=control.data_view,
        name=f'controlGroup_{control_id}:rangeSliderDataView',
    )

    return kbn_control, reference


@log_compile
def compile_time_slider_control(order: int, *, control: TimeSliderControl) -> KbnTimeSliderControl:
    """Compile a TimeSliderControl into its Kibana view model representation.

    Args:
        order (int): The order of the control in the dashboard.
        control (TimeSliderControl): The TimeSliderControl object to compile.

    Returns:
        KbnTimeSliderControl: The compiled Kibana time slider control view model.

    """
    return KbnTimeSliderControl(
        grow=True,
        order=order,
        width=default_if_none(control.width, 'medium'),
        explicitInput=KbnTimeSliderControlExplicitInput(
            id=control.get_id(),
            timesliceEndAsPercentageOfTimeRange=default_if_none(control.end_offset, 100.0),
            timesliceStartAsPercentageOfTimeRange=default_if_none(control.start_offset, 0.0),
        ),
    )


@log_compile
def compile_esql_field_control(order: int, *, control: ESQLFieldControl) -> KbnESQLControl:
    """Compile an ESQLFieldControl into its Kibana view model representation."""
    selected_options = [control.default] if control.default is not None else []

    return KbnESQLControl(
        grow=False,
        order=order,
        width=default_if_none(control.width, 'medium'),
        explicitInput=KbnESQLControlExplicitInput(
            id=control.get_id(),
            variableName=control.variable_name,
            variableType=control.variable_type,
            esqlQuery='',
            controlType=EsqlControlType.STATIC_VALUES.value,
            title=control.label,
            selectedOptions=selected_options,
            singleSelect=True,
            availableOptions=control.choices,
        ),
    )


@log_compile
def compile_esql_function_control(order: int, *, control: ESQLFunctionControl) -> KbnESQLControl:
    """Compile an ESQLFunctionControl into its Kibana view model representation."""
    selected_options = [control.default] if control.default is not None else []

    return KbnESQLControl(
        grow=False,
        order=order,
        width=default_if_none(control.width, 'medium'),
        explicitInput=KbnESQLControlExplicitInput(
            id=control.get_id(),
            variableName=control.variable_name,
            variableType=control.variable_type,
            esqlQuery='',
            controlType=EsqlControlType.STATIC_VALUES.value,
            title=control.label,
            selectedOptions=selected_options,
            singleSelect=True,
            availableOptions=control.choices,
        ),
    )


@log_compile
def compile_esql_static_single_select_control(order: int, *, control: ESQLStaticSingleSelectControl) -> KbnESQLControl:
    """Compile an ESQLStaticSingleSelectControl into its Kibana view model representation."""
    selected_options = [control.default] if control.default is not None else []

    return KbnESQLControl(
        grow=False,
        order=order,
        width=default_if_none(control.width, 'medium'),
        explicitInput=KbnESQLControlExplicitInput(
            id=control.get_id(),
            variableName=control.variable_name,
            variableType=control.variable_type,
            esqlQuery='',
            controlType=EsqlControlType.STATIC_VALUES.value,
            title=control.label,
            selectedOptions=selected_options,
            singleSelect=True,
            availableOptions=control.choices,
        ),
    )


@log_compile
def compile_esql_static_multi_select_control(order: int, *, control: ESQLStaticMultiSelectControl) -> KbnESQLControl:
    """Compile an ESQLStaticMultiSelectControl into its Kibana view model representation."""
    selected_options = control.default if control.default is not None else []

    return KbnESQLControl(
        grow=False,
        order=order,
        width=default_if_none(control.width, 'medium'),
        explicitInput=KbnESQLControlExplicitInput(
            id=control.get_id(),
            variableName=control.variable_name,
            variableType=control.variable_type,
            esqlQuery='',
            controlType=EsqlControlType.STATIC_VALUES.value,
            title=control.label,
            selectedOptions=selected_options,
            singleSelect=False,
            availableOptions=control.choices,
        ),
    )


@log_compile
def compile_esql_query_single_select_control(order: int, *, control: ESQLQuerySingleSelectControl) -> KbnESQLControl:
    """Compile an ESQLQuerySingleSelectControl into its Kibana view model representation."""
    selected_options = [control.default] if control.default is not None else []

    return KbnESQLControl(
        grow=False,
        order=order,
        width=default_if_none(control.width, 'medium'),
        explicitInput=KbnESQLControlExplicitInput(
            id=control.get_id(),
            variableName=control.variable_name,
            variableType=control.variable_type,
            esqlQuery=control.query,
            controlType=EsqlControlType.VALUES_FROM_QUERY.value,
            title=control.label,
            selectedOptions=selected_options,
            singleSelect=True,
            availableOptions=None,
        ),
    )


@log_compile
def compile_esql_query_multi_select_control(order: int, *, control: ESQLQueryMultiSelectControl) -> KbnESQLControl:
    """Compile an ESQLQueryMultiSelectControl into its Kibana view model representation."""
    selected_options = control.default if control.default is not None else []

    return KbnESQLControl(
        grow=False,
        order=order,
        width=default_if_none(control.width, 'medium'),
        explicitInput=KbnESQLControlExplicitInput(
            id=control.get_id(),
            variableName=control.variable_name,
            variableType=control.variable_type,
            esqlQuery=control.query,
            controlType=EsqlControlType.VALUES_FROM_QUERY.value,
            title=control.label,
            selectedOptions=selected_options,
            singleSelect=False,
            availableOptions=None,
        ),
    )


@log_compile
def compile_control(order: int, *, control: ControlTypes) -> tuple[KbnControlTypes, KbnReference | None]:  # noqa: PLR0911
    """Compile a single control into its Kibana view model representation.

    Args:
        order: The order of the control in the dashboard.
        control: The control object to compile.

    Returns:
        tuple: A tuple containing the compiled control and an optional data view reference.

    """
    if isinstance(control, OptionsListControl):
        return compile_options_list_control(order, control=control)

    if isinstance(control, TimeSliderControl):
        return compile_time_slider_control(order, control=control), None

    if isinstance(control, RangeSliderControl):
        return compile_range_slider_control(order, control=control)

    if isinstance(control, ESQLFieldControl):
        return compile_esql_field_control(order, control=control), None

    if isinstance(control, ESQLFunctionControl):
        return compile_esql_function_control(order, control=control), None

    if isinstance(control, ESQLStaticSingleSelectControl):
        return compile_esql_static_single_select_control(order, control=control), None

    if isinstance(control, ESQLStaticMultiSelectControl):
        return compile_esql_static_multi_select_control(order, control=control), None

    if isinstance(control, ESQLQuerySingleSelectControl):
        return compile_esql_query_single_select_control(order, control=control), None

    if isinstance(control, ESQLQueryMultiSelectControl):  # pyright: ignore[reportUnnecessaryIsInstance]
        return compile_esql_query_multi_select_control(order, control=control), None

    msg = f'Unknown control type: {type(control).__name__}'  # pyright: ignore[reportUnreachable]
    raise TypeError(msg)


@log_compile
def compile_control_panels_json(controls: Sequence[ControlTypes]) -> tuple[KbnControlPanelsJson, list[KbnReference]]:
    """Compile the control group input for a Dashboard object into its Kibana view model representation.

    Args:
        controls (Sequence[ControlTypes]): The sequence of control objects to compile.

    Returns:
        tuple: A tuple containing the control panels JSON and a list of references.

    """
    kbn_control_panels_json: KbnControlPanelsJson = KbnControlPanelsJson()
    references: list[KbnReference] = []

    for i, config_control in enumerate(iterable=controls):
        kbn_control, reference = compile_control(i, control=config_control)

        kbn_control_id: str = kbn_control.explicitInput.id

        kbn_control_panels_json.add(key=kbn_control_id, value=kbn_control)

        if reference is not None:
            references.append(reference)

    return kbn_control_panels_json, references


@log_compile
def compile_control_group(
    *, control_settings: ControlSettings, controls: Sequence[ControlTypes]
) -> tuple[KbnControlGroupInput, list[KbnReference]]:
    """Compile a control group from a sequence of ControlTypes into a Kibana view model.

    Args:
        control_settings (ControlSettings): The settings for the control group.
        controls (Sequence[ControlTypes]): The sequence of control configurations to compile.

    Returns:
        tuple: A tuple containing the control group input and a list of data view references.

    """
    panels_json, references = compile_control_panels_json(controls)

    # Kibana's control API uses "ignore" semantics (ignoreFilters, ignoreQuery, etc.)
    # but our config uses "apply" semantics for better UX. We invert the booleans here:
    # - apply_global_filters: True  → ignoreFilters: False  (respect filters)
    # - apply_global_filters: False → ignoreFilters: True   (ignore filters)
    ignore_parent_settings_json = KbnIgnoreParentSettingsJson(
        ignoreFilters=return_if(var=control_settings.apply_global_filters, is_true=False, is_false=True, default=False),
        ignoreQuery=return_if(var=control_settings.apply_global_filters, is_true=False, is_false=True, default=False),
        ignoreTimerange=return_if(var=control_settings.apply_global_timerange, is_true=False, is_false=True, default=False),
        ignoreValidations=return_if(var=control_settings.ignore_zero_results, is_true=True, is_false=False, default=False),
    )

    control_group_input = KbnControlGroupInput(
        chainingSystem=return_if(
            var=control_settings.chain_controls,
            is_false=ChainingSystemEnum.NONE,
            is_true=ChainingSystemEnum.HIERARCHICAL,
            default=ChainingSystemEnum.HIERARCHICAL,
        ),
        controlStyle=return_if_equals(
            var=control_settings.label_position,
            equals='inline',
            is_true=ControlStyleEnum.ONE_LINE,
            is_false=ControlStyleEnum.TWO_LINE,
            is_none=ControlStyleEnum.ONE_LINE,
        ),
        ignoreParentSettingsJSON=ignore_parent_settings_json,
        panelsJSON=panels_json,
        showApplySelections=control_settings.click_to_apply or False,
    )

    return control_group_input, references
