#  Copyright 2026 European Union
#  Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
#  SPDX-License-Identifier: EUPL-1.2
"""Manager widget for filter configuration.

:Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
:Description: Combines a FilterTreeView and a stack of editors to manage
              processor filters.
"""

from __future__ import annotations

from PySide6.QtCore import QModelIndex, Qt, Signal
from PySide6.QtWidgets import QPushButton, QSplitter, QStackedWidget, QVBoxLayout, QWidget

from mafw.steering.models import (
    ConditionalFilterConfig,
    FieldFilterConfig,
    FilterConfig,
    ModelFilterConfig,
    ProcessorConfig,
)
from mafw.steering_gui.models.filter_tree_model import FilterNodeType, FilterTreeModel
from mafw.steering_gui.views.filter_editors import (
    ConditionalFilterEditor,
    ConditionEditor,
    FieldFilterEditor,
    LogicEditor,
    ModelFilterEditor,
    ProcessorFilterEditor,
)
from mafw.steering_gui.views.filter_tree import FilterTreeView


class FilterManager(QWidget):
    """Widget for managing processor filters."""

    finished = Signal()
    filter_changed = Signal()

    def __init__(self, config: ProcessorConfig, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self._config = config

        self._tree = FilterTreeView()
        self._model = FilterTreeModel(config)
        self._tree.set_model(self._model)
        self._tree.selection_changed.connect(self._on_selection_changed)
        self._tree.filter_changed.connect(self.filter_changed)
        self._tree.filter_changed.connect(self._refresh_processor_editor_if_visible)

        self._stack = QStackedWidget()

        # Create editors
        self._processor_editor = ProcessorFilterEditor()
        self._model_editor = ModelFilterEditor()
        self._field_editor = FieldFilterEditor()
        self._condition_editor = ConditionEditor()
        self._conditional_editor = ConditionalFilterEditor()
        self._logic_editor = LogicEditor()
        self._processor_editor.set_config(self._config)

        self._stack.addWidget(self._processor_editor)
        self._stack.addWidget(self._model_editor)
        self._stack.addWidget(self._field_editor)
        self._stack.addWidget(self._condition_editor)
        self._stack.addWidget(self._conditional_editor)
        self._stack.addWidget(self._logic_editor)

        # Connect processor editor signals
        self._processor_editor.add_model_filter_requested.connect(self._on_add_model_filter_requested)
        self._processor_editor.remove_model_filter_requested.connect(self._on_remove_model_filter_requested)
        self._processor_editor.edit_model_filter_requested.connect(self._on_edit_model_filter_requested)
        self._processor_editor.add_logic_requested.connect(self._tree.add_root_logic)

        # Connect model editor signals
        self._model_editor.add_condition_requested.connect(self._tree.add_condition)
        self._model_editor.add_field_filter_requested.connect(self._tree.add_field_filter)
        self._model_editor.add_logic_requested.connect(self._tree.add_logic)
        self._model_editor.remove_requested.connect(self._on_model_remove_requested)
        self._model_editor.edit_condition_requested.connect(self._on_edit_condition_requested)
        self._model_editor.edit_field_filter_requested.connect(self._on_edit_field_filter_requested)
        self._model_editor.remove_condition_requested.connect(self._on_remove_condition_requested)
        self._model_editor.filter_changed.connect(self.filter_changed)
        self._model_editor.enabled_changed.connect(self._refresh_tree_for_config)

        # Connect field editor signals
        self._field_editor.add_condition_requested.connect(self._tree.add_condition)
        self._field_editor.remove_requested.connect(self._on_model_remove_requested)
        self._field_editor.add_logic_requested.connect(self._tree.add_logic)
        self._field_editor.edit_condition_requested.connect(self._on_edit_condition_requested)
        self._field_editor.remove_condition_requested.connect(self._on_remove_condition_requested)
        self._field_editor.filter_changed.connect(self.filter_changed)
        self._field_editor.enabled_changed.connect(self._refresh_tree_for_config)

        # Connect condition editor signals
        self._condition_editor.condition_changed.connect(self._on_condition_changed)
        self._condition_editor.condition_renamed.connect(self._on_condition_renamed)
        self._condition_editor.delete_requested.connect(self._on_remove_requested)
        self._condition_editor.add_condition_requested.connect(self._on_add_condition_requested)
        self._condition_editor.add_logic_requested.connect(self._on_add_logic_requested)

        # Connect conditional editor signals
        self._conditional_editor.filter_changed.connect(self._on_conditional_filter_changed)
        self._conditional_editor.remove_requested.connect(self._on_remove_requested)
        self._conditional_editor.enabled_changed.connect(self._refresh_tree_for_config)
        self._logic_editor.filter_changed.connect(self.filter_changed)
        self._logic_editor.remove_requested.connect(self._on_logic_remove_requested)
        self._logic_editor.validation_changed.connect(self._on_logic_validation_changed)

        # Layout
        splitter = QSplitter()
        splitter.addWidget(self._tree)
        splitter.addWidget(self._stack)
        splitter.setStretchFactor(1, 1)

        back_button = QPushButton('Back to Parameters')
        back_button.clicked.connect(self.finished)

        layout = QVBoxLayout(self)
        layout.addWidget(back_button)
        layout.addWidget(splitter)

    def _on_model_remove_requested(self, index: QModelIndex) -> None:
        if self._tree.remove_item(index):
            self._processor_editor.set_config(self._config)
            self._stack.setCurrentWidget(self._processor_editor)

    def _on_remove_requested(self) -> None:
        index = self._tree.currentIndex()
        if index.isValid():
            if self._tree.remove_item(index):
                self._processor_editor.set_config(self._config)
                self._stack.setCurrentWidget(self._processor_editor)

    def _on_add_condition_requested(self) -> None:
        index = self._tree.currentIndex()
        if index.isValid():
            parent_index = self._model.parent(index)
            if parent_index.isValid():
                self._tree.add_condition(parent_index)

    def _on_add_logic_requested(self) -> None:
        index = self._tree.currentIndex()
        if index.isValid():
            parent_index = self._model.parent(index)
            if parent_index.isValid():
                self._tree.add_logic(parent_index)

    def _on_edit_condition_requested(self, model_index: QModelIndex, field_name: str) -> None:
        model_data = self._model.get_item_data(model_index)
        if isinstance(model_data, (ModelFilterConfig, FieldFilterConfig)):
            key = self._model.condition_key_for_parent(model_data, field_name)
            if key:
                index = self._model.find_condition_index(key)
                if index.isValid():
                    self._tree.setCurrentIndex(index)

    def _on_edit_field_filter_requested(self, model_index: QModelIndex, field_name: str) -> None:
        model_data = self._model.get_item_data(model_index)
        model_name = model_data.model if isinstance(model_data, ModelFilterConfig) else str(model_data)

        filters = self._config.filters.get(model_name, [])
        for f in filters:
            if isinstance(f, FieldFilterConfig) and f.field_name == field_name:
                index = self._model.find_index_for_data(f)
                if index.isValid():
                    self._tree.setCurrentIndex(index)
                break

    def _on_remove_condition_requested(self, model_index: QModelIndex, field_name: str) -> None:
        model_data = self._model.get_item_data(model_index)
        if isinstance(model_data, (ModelFilterConfig, FieldFilterConfig)):
            key = self._model.condition_key_for_parent(model_data, field_name)
            if key:
                index = self._model.find_condition_index(key)
                if index.isValid():
                    if self._tree.remove_item(index):
                        # Refresh editor table
                        if isinstance(model_data, ModelFilterConfig):
                            self._model_editor.set_config(model_data, self._config, model_index)
                        else:
                            self._field_editor.set_config(model_data, self._config, model_index)

    def _on_selection_changed(self, index: QModelIndex) -> None:
        if not index.isValid():
            self._processor_editor.set_config(self._config)
            self._stack.setCurrentWidget(self._processor_editor)
            return

        node_type = self._model.get_node_type(index)

        if node_type == FilterNodeType.ROOT:
            self._processor_editor.set_config(self._config)
            self._stack.setCurrentWidget(self._processor_editor)
        elif node_type == FilterNodeType.MODEL:
            data = self._model.get_item_data(index)
            self._model_editor.set_config(data, self._config, index)
            self._stack.setCurrentWidget(self._model_editor)
        elif node_type == FilterNodeType.FIELD:
            data = self._model.get_item_data(index)
            self._field_editor.set_config(data, self._config, index)
            self._stack.setCurrentWidget(self._field_editor)
        elif node_type == FilterNodeType.CONDITION:
            condition = self._model.get_item_data(index)
            parent_index = self._model.parent(index)
            parent_data = self._model.get_item_data(parent_index)
            item = index.internalPointer()
            if isinstance(parent_data, (ModelFilterConfig, FieldFilterConfig)) and item is not None:
                condition_name = getattr(item, 'key', None)
                if isinstance(condition_name, str):
                    self._condition_editor.set_condition(condition, parent_data, condition_name)
                    self._stack.setCurrentWidget(self._condition_editor)
                    key = self._model.condition_key_for_parent(parent_data, condition_name)
                    if key is not None:
                        self._model.set_condition_warning(key, not self._condition_editor.is_complete())
                    return
            self._processor_editor.set_config(self._config)
            self._stack.setCurrentWidget(self._processor_editor)
        elif node_type == FilterNodeType.CONDITIONAL:
            data = self._model.get_item_data(index)
            if isinstance(data, ConditionalFilterConfig):
                self._conditional_editor.set_config(data, self._config, index)
                self._stack.setCurrentWidget(self._conditional_editor)
                key = self._model.condition_key_for_item(index.internalPointer())
                if key:
                    self._model.set_condition_warning(key, not data.is_complete())
                return
            self._processor_editor.set_config(self._config)
            self._stack.setCurrentWidget(self._processor_editor)
        elif node_type == FilterNodeType.LOGIC:
            data = self._model.get_item_data(index)
            if isinstance(data, (ProcessorConfig, ModelFilterConfig, FieldFilterConfig)):
                self._logic_editor.set_config(data, self._config, index)
                self._stack.setCurrentWidget(self._logic_editor)
                logic_key = self._model.logic_key_for_index(index)
                if logic_key:
                    self._model.set_logic_warning(logic_key, not self._logic_editor.is_valid())
                return
            self._processor_editor.set_config(self._config)
            self._stack.setCurrentWidget(self._processor_editor)
        else:
            self._processor_editor.set_config(self._config)
            self._stack.setCurrentWidget(self._processor_editor)

    def _refresh_tree_for_config(self, config: FilterConfig) -> None:
        index = self._model.find_index_for_data(config)
        if not index.isValid():
            return
        indexes = [index] + self._model.collect_subtree_indexes(index)
        roles = [Qt.ItemDataRole.FontRole, Qt.ItemDataRole.ForegroundRole]
        for idx in indexes:
            self._model.dataChanged.emit(idx, idx, roles)

    def _on_conditional_filter_changed(self) -> None:
        """Handle changes in the ConditionalFilterEditor."""
        if self._stack.currentWidget() is not self._conditional_editor:
            return
        current_index = self._tree.currentIndex()
        if not current_index.isValid():
            return
        data = self._model.get_item_data(current_index)
        if not isinstance(data, ConditionalFilterConfig):
            return

        key = self._model.condition_key_for_item(current_index.internalPointer())
        if key:
            self._model.set_condition_warning(key, not data.is_complete())

        self.filter_changed.emit()

    def _on_logic_remove_requested(self, index: QModelIndex) -> None:
        if self._tree.remove_item(index):
            self._processor_editor.set_config(self._config)
            self._stack.setCurrentWidget(self._processor_editor)

    def _on_logic_validation_changed(self, index: QModelIndex, is_valid: bool) -> None:
        logic_key = self._model.logic_key_for_index(index)
        data = self._model.get_item_data(index)
        if isinstance(data, (ProcessorConfig, ModelFilterConfig, FieldFilterConfig)):
            data.logic_is_valid = is_valid
        if logic_key:
            self._model.set_logic_warning(logic_key, not is_valid)

    def _on_condition_changed(self) -> None:
        if self._stack.currentWidget() is not self._condition_editor:
            return
        current_index = self._tree.currentIndex()
        if not current_index.isValid():
            return
        key = self._model.condition_key_for_index(current_index)
        if key is None:
            return
        self._model.set_condition_warning(key, not self._condition_editor.is_complete())
        self.filter_changed.emit()

    def _on_condition_renamed(self, old_name: str, new_name: str) -> None:
        current_index = self._tree.currentIndex()
        if not current_index.isValid():
            return
        parent_index = self._model.parent(current_index)
        parent_data = self._model.get_item_data(parent_index)
        if not isinstance(parent_data, (ModelFilterConfig, FieldFilterConfig)):
            return
        if new_name in parent_data.conditions and new_name != old_name:
            self._condition_editor.set_condition(parent_data.conditions[old_name], parent_data, old_name)
            return
        condition = parent_data.conditions.pop(old_name, None)
        if condition is None:
            return
        parent_data.conditions[new_name] = condition
        old_key = self._model.condition_key_for_parent(parent_data, old_name)
        new_key = self._model.condition_key_for_parent(parent_data, new_name)
        if old_key is not None and new_key is not None:
            was_warning = self._model.is_condition_warning(old_key)
            self._model.set_condition_warning(old_key, False)
            self._model.set_condition_warning(new_key, was_warning)
        self._model.refresh()
        self._tree.expandAll()
        if new_key is not None:
            new_index = self._model.find_condition_index(new_key)
            if new_index.isValid():
                self._tree.setCurrentIndex(new_index)
        self.filter_changed.emit()

    def refresh(self) -> None:
        """Refresh the tree model."""
        self._model.refresh()
        self._tree.expandAll()

    def _on_add_model_filter_requested(self, model_name: str) -> None:
        self._tree.add_model_filter_by_name(model_name)

    def _on_remove_model_filter_requested(self, model_name: str) -> None:
        index = self._model.find_model_index(model_name)
        if index.isValid():
            if self._tree.remove_item(index):
                self._processor_editor.set_config(self._config)
                self._stack.setCurrentWidget(self._processor_editor)

    def _on_edit_model_filter_requested(self, model_name: str) -> None:
        index = self._model.find_model_index(model_name)
        if index.isValid():
            self._tree.setCurrentIndex(index)

    def _refresh_processor_editor_if_visible(self) -> None:
        if self._stack.currentWidget() is self._processor_editor:
            self._processor_editor.set_config(self._config)
