"""Модуль страницы создания дочернего элемента.

Содержит класс для работы с формой создания дочернего элемента.
"""

from playwright.sync_api import Page, expect
from elements.tooltip_button_element import TooltipButton
from components.toolbar_component import ToolbarComponent
from components.dropdown_list_component import DropdownList
from pages.base_page import BasePage
from tools.logger import get_logger

logger = get_logger("CREATE_CHILD_ELEMENT")

# =============== Локаторы ================================================
PANEL_HEADER = "//span[text()='Объекты']/following-sibling::i"
TOOLBAR_CONTENT = "//div[@class='v-toolbar__content']"
CREATE_BUTTON_ANCESTOR_DIV3 = "xpath=/ancestor::div[3]//button"
PANEL_HEADER_ANCESTOR_DIV2 = "xpath=/ancestor::div[2]"

CREATE_CHILD_TITLE = "//div[contains(@class, 'v-toolbar__title') and contains(., 'Создать дочерний элемент в')]"
OBJECT_CLASS_COMBOBOX = "//div[@role='combobox' and .//label[text()='Класс объекта учета']]"
CANCEL_BUTTON = "//div[contains(@class, 'v-toolbar__title') and contains(., 'Создать дочерний элемент в')]/..//button[contains(@class, 'v-btn--icon')]"

# Локаторы для работы с combobox
COMBOBOX_LABEL = "label"
COMBOBOX_INPUT = "input[name='entity']"
COMBOBOX_ICON = ".v-input__icon--append"
COMBOBOX_ICON_ARROW = ".v-input__icon--append .mdi-menu-down"

# Локаторы для выпадающего списка combobox - уточненные
LISTBOX_SELECTOR = "//div[contains(@class, 'v-menu__content')]//div[@role='list']"
OPTIONS_SELECTOR = "//div[contains(@class, 'v-menu__content')]//div[@role='listitem']//span"

# Локаторы для получения выбранного значения
SELECTED_VALUE_SPAN = "span"
#========================================================================================================


class CreateChildElementTab(BasePage):
    """Класс для работы с формой создания дочернего элемента."""

    def __init__(self, page: Page) -> None:
        """
        Инициализирует объект формы создания дочернего элемента.

        Args:
            page: Экземпляр страницы Playwright
        """
        super().__init__(page)

        # Локаторы для кнопок
        panel_header_locator = self.page.locator(PANEL_HEADER)

        # Кнопка "Создать" - первая кнопка в тулбаре
        create_button_locator = panel_header_locator.locator(CREATE_BUTTON_ANCESTOR_DIV3).nth(0)

        # Кнопка "Отменить" - ищем глобально на странице
        cancel_button_locator = self.page.locator(CANCEL_BUTTON)

        # Инициализация кнопок
        self.create_button = TooltipButton(page, create_button_locator, "add")
        self.cancel_button = TooltipButton(page, cancel_button_locator, "cancel")

        # Инициализация тулбара с обеими кнопками
        self.toolbar = ToolbarComponent(page, "")
        self.toolbar.add_tooltip_button(create_button_locator, "add")
        self.toolbar.add_tooltip_button(cancel_button_locator, "cancel")

        # Инициализация компонента выпадающего списка
        self.dropdown = DropdownList(page)

    def get_toolbar_title(self) -> list[str]:
        """
        Получает заголовок панели инструментов.

        Returns:
            list[str]: Список элементов заголовка панели инструментов
        """
        toolbar_title_locator = self.page.locator(PANEL_HEADER).\
                         locator(PANEL_HEADER_ANCESTOR_DIV2).get_by_role("navigation").\
                         locator(TOOLBAR_CONTENT)

        return self.toolbar.get_toolbar_composite_title_text(toolbar_title_locator)

    def should_be_toolbar_buttons(self) -> None:
        """
        Проверяет наличие и функциональность кнопок тулбара.

        Raises:
            AssertionError: Если кнопки недоступны или подсказки неверны.
        """

        self.wait_for_timeout(2000)

        self.toolbar.check_button_visibility("cancel")
        self.toolbar.check_button_tooltip("cancel", "Отменить")
        self.toolbar.get_button_by_name("cancel").click()
        self.wait_for_timeout(2000)

    def click_create_button(self) -> None:
        """
        Кликает на кнопку 'Создать'.
        """
        logger.info("Клик на кнопку 'Создать'...")
        self.toolbar.get_button_by_name("add").click()

    def click_cancel_button(self) -> None:
        """
        Кликает на кнопку 'Отменить'.
        """
        logger.info("Клик на кнопку 'Отменить'...")
        self.toolbar.get_button_by_name("cancel").click()

    def check_toolbar_title(self, expected_title: str) -> None:
        """
        Проверяет заголовок тулбара.

        Args:
            expected_title: Ожидаемый заголовок тулбара

        Raises:
            AssertionError: Если заголовок не соответствует ожидаемому
        """
        # Используем метод тулбара с нашим специфичным локатором
        self.toolbar.check_toolbar_presence_by_locator(CREATE_CHILD_TITLE,
                                                    f"Заголовок тулбара '{expected_title}' не найден")

        # Получаем текст и проверяем его
        actual_text = self.toolbar.get_toolbar_title_text(CREATE_CHILD_TITLE)
        assert expected_title in actual_text, f"Заголовок не совпадает. Ожидалось: '{expected_title}', Получено: '{actual_text}'"

        logger.info(f"Заголовок тулбара корректен: '{actual_text}'")

    def check_object_class_combobox_presence(self) -> None:
        """
        Проверяет наличие combobox 'Класс объекта учета'.

        Raises:
            AssertionError: Если combobox не найден
        """
        logger.info("Проверка наличия combobox 'Класс объекта учета'...")

        combobox_locator = self.page.locator(OBJECT_CLASS_COMBOBOX)
        expect(combobox_locator).to_be_visible()

        logger.info("Combobox 'Класс объекта учета' найден")

    def check_object_class_combobox_content(self) -> None:
        """
        Проверяет содержимое combobox 'Класс объекта учета'.

        Raises:
            AssertionError: Если содержимое не соответствует ожидаемому
        """
        logger.info("Проверка содержимого combobox 'Класс объекта учета'...")

        combobox_locator = self.page.locator(OBJECT_CLASS_COMBOBOX)

        # Проверяем что combobox видим
        expect(combobox_locator).to_be_visible()

        # Проверяем наличие label
        label_locator = combobox_locator.locator(COMBOBOX_LABEL)
        expect(label_locator).to_have_text("Класс объекта учета")

        # Проверяем наличие input поля
        input_locator = combobox_locator.locator(COMBOBOX_INPUT)
        expect(input_locator).to_be_visible()

        # Для combobox нормально иметь readonly атрибут - это стандартное поведение
        # Проверяем что поле доступно для выбора (не disabled)
        expect(input_locator).not_to_have_attribute("disabled", "disabled")

        # Проверяем наличие иконки стрелки
        icon_locator = combobox_locator.locator(COMBOBOX_ICON_ARROW)
        expect(icon_locator).to_be_visible()

        logger.info("Содержимое combobox 'Класс объекта учета' корректно")

    def open_object_class_combobox(self) -> None:
        """
        Открывает выпадающий список combobox 'Класс объекта учета'.
        """
        logger.info("Открытие combobox 'Класс объекта учета'...")

        combobox_locator = self.page.locator(OBJECT_CLASS_COMBOBOX)
        listbox_locator = self.page.locator(LISTBOX_SELECTOR)
        icon_locator = combobox_locator.locator(COMBOBOX_ICON)

        # Проверяем, не открыт ли уже список
        listbox_already_open = False
        listbox_count = listbox_locator.count()

        if listbox_count > 0:
            listbox_already_open = listbox_locator.first.is_visible()

        if not listbox_already_open:
            # Только если список не открыт, кликаем на иконку
            icon_locator.click(timeout=10000)
            logger.info("Клик на иконку combobox выполнен")
            self.wait_for_timeout(1000)

        # Проверяем что список открылся
        listbox_count_after = listbox_locator.count()
        listbox_visible = False

        if listbox_count_after > 0:
            listbox_visible = listbox_locator.first.is_visible()

        if listbox_visible:
            logger.info("Выпадающий список найден и открыт")
        else:
            logger.warning("Не удалось открыть выпадающий список")

    def get_object_class_options(self) -> list[str]:
        """
        Получает список доступных опций из combobox.

        Returns:
            list[str]: Список доступных классов объектов
        """
        logger.info("Получение списка опций combobox 'Класс объекта учета'...")

        # Открываем combobox (если еще не открыт)
        self.open_object_class_combobox()

        # Используем метод get_item_names из DropdownList
        options_list = self.dropdown.get_item_names(LISTBOX_SELECTOR)

        # Закрываем combobox (кликаем вне его)
        self.page.mouse.click(10, 10)
        self.wait_for_timeout(500)

        logger.info(f"Найдено опций: {len(options_list)} - {options_list}")
        return options_list

    def select_object_class(self, class_name: str) -> None:
        """
        Выбирает класс объекта из выпадающего списка.

        Args:
            class_name: Название класса объекта для выбора

        Raises:
            AssertionError: Если класс не найден в списке
        """
        logger.info(f"Выбор класса объекта: '{class_name}'...")

        # Открываем combobox
        self.open_object_class_combobox()

        self.dropdown.click_item_with_text(class_name)

        # Проверяем что выбор произошел
        self.wait_for_timeout(1000)
        selected_value = self.get_selected_object_class()

        if class_name.lower() not in selected_value.lower() and selected_value.lower() not in class_name.lower():
            # Если выбор не произошел, получаем доступные опции для отладки
            available_options = self.get_object_class_options()
            logger.warning(f"Класс '{class_name}' не выбран. Текущее значение: '{selected_value}'. Доступные опции: {available_options}")
            raise AssertionError(f"Не удалось выбрать класс объекта '{class_name}'")

        logger.info(f"Класс объекта '{class_name}' успешно выбран")

    def get_selected_object_class(self) -> str:
        """
        Получает выбранный класс объекта учета.

        Returns:
            str: Выбранный класс объекта или пустая строка если ничего не выбрано
        """
        combobox_locator = self.page.locator(OBJECT_CLASS_COMBOBOX)

        selected_value = ""

        # Ищем в span элементах
        span_locator = combobox_locator.locator(SELECTED_VALUE_SPAN)
        if span_locator.count() > 0:
            for i in range(span_locator.count()):
                span_text = span_locator.nth(i).text_content().strip()
                if span_text and span_text not in ["Класс объекта учета"]:
                    selected_value = span_text
                    break

        logger.info(f"Выбранный класс объекта: '{selected_value}'")
        return selected_value

    def check_object_class_selected(self, expected_class: str) -> None:
        """
        Проверяет что выбран указанный класс объекта.

        Args:
            expected_class: Ожидаемый выбранный класс объекта

        Raises:
            AssertionError: Если выбранный класс не соответствует ожидаемому
        """
        logger.info(f"Проверка выбранного класса объекта: '{expected_class}'...")

        # Даем время на обновление значения
        self.wait_for_timeout(1000)

        actual_class = self.get_selected_object_class()

        # Проверка - допускаем частичное совпадение
        if expected_class.lower() in actual_class.lower() or actual_class.lower() in expected_class.lower():
            logger.info(f"Класс объекта '{expected_class}' успешно выбран (фактически: '{actual_class}')")
        else:
            raise AssertionError(f"Выбранный класс не соответствует ожидаемому. Ожидалось: '{expected_class}', Получено: '{actual_class}'")

    def check_object_class_options_content(self, expected_options: list = None) -> None:
        """
        Проверяет содержимое списка опций combobox.

        Args:
            expected_options: Ожидаемый список опций. Если None, проверяет только что список не пустой.

        Raises:
            AssertionError: Если список опций не соответствует ожидаемому
        """
        logger.info("Проверка содержимого списка опций combobox...")

        # Получаем доступные опции
        available_options = self.get_object_class_options()

        if expected_options is not None:
            # Проверяем соответствие ожидаемому списку
            assert set(available_options) == set(expected_options), (
                f"Список опций не соответствует ожидаемому. "
                f"Ожидалось: {expected_options}, Получено: {available_options}"
            )
        else:
            # Проверяем что список не пустой
            assert len(available_options) > 0, "Список опций combobox пустой"

        logger.info(f"Содержимое списка опций корректно: {available_options}")

    def check_dropdown_item_presence(self, item_text: str) -> None:
        """
        Проверяет наличие элемента в выпадающем списке.

        Args:
            item_text: Текст элемента для проверки
        """
        logger.info(f"Проверка наличия элемента '{item_text}' в выпадающем списке...")

        # Получаем все опции и проверяем наличие
        available_options = self.get_object_class_options()

        if item_text not in available_options:
            raise AssertionError(f"Элемент '{item_text}' не найден в списке опций. Доступные опции: {available_options}")

        logger.info(f"Элемент '{item_text}' присутствует в списке")
