"""Базовый модуль для работы с формами стойки."""

import time
from dataclasses import dataclass
from typing import Optional, List, Dict, Any, Tuple
from abc import ABC, abstractmethod
from playwright.sync_api import Page
from tools.logger import get_logger
from elements.text_input_element import TextInput
from components.base_component import BaseComponent
from components.dropdown_list_component import DropdownList

logger = get_logger("BASE_RACK_FORM")
logger.setLevel("INFO")

@dataclass
class BaseRackData:
    """Базовый класс для хранения данных стойки."""

    # Основные поля
    name: str = ""
    serial: str = ""
    inventory: str = ""
    comment: str = ""

    # Combobox поля
    cable_entry: str = ""
    state: str = ""
    depth: str = ""
    usize: str = ""

    # Combobox поля (справочники)
    owner: str = ""
    service_org: str = ""
    project: str = ""


class BaseRackForm(BaseComponent, ABC):
    """Базовый компонент для работы с формами стойки."""

    # Маппинг текстовых полей (должен быть переопределен в наследниках)
    TEXT_FIELDS_MAPPING: Dict[str, Tuple[str, str]] = {}
    TEXT_FIELDS_LOCATORS: Dict[str, str] = {}

    # Маппинг combobox полей (должен быть переопределен в наследниках)
    COMBOBOX_FIELDS_MAPPING: Dict[str, Tuple[str, str, str]] = {}
    COMBOBOX_FIELDS_LOCATORS: Dict[str, str] = {}

    # Дополнительные типы полей (checkbox и т.д.) - опционально
    CHECKBOX_FIELDS_MAPPING: Dict[str, Tuple[str, str]] = {}
    CHECKBOX_FIELDS_LOCATORS: Dict[str, str] = {}

    def __init__(self, page: Page, form_container_locator: str) -> None:
        """Инициализирует базовый компонент формы стойки.

        Args:
            page: Экземпляр страницы Playwright
            form_container_locator: Локатор контейнера формы
        """
        super().__init__(page)
        self.page = page
        self.form_container_locator = form_container_locator
        self.content_items: Dict[str, Any] = {}
        self.available_fields = None

        # Инициализация полей формы
        self._init_form_fields()

    def _init_form_fields(self) -> None:
        """Инициализирует все поля формы."""
        container_locator = self.page.locator(self.form_container_locator)
        if container_locator.count() > 0:
            self.available_fields = self.get_input_fields_locators(container_locator)

        self._init_text_fields()
        self._init_combobox_fields()
        self._init_checkbox_fields()

    def _init_text_fields(self) -> None:
        """Инициализирует текстовые поля формы."""
        for field_label, (attr_name, widget_name) in self.TEXT_FIELDS_MAPPING.items():
            locator = self.TEXT_FIELDS_LOCATORS.get(field_label)
            if not locator:
                continue
            self._init_single_text_field(field_label, locator, widget_name)

    def _init_single_text_field(self, field_label: str, locator: str, widget_name: str) -> None:
        """Инициализирует одно текстовое поле."""
        try:
            element = self.page.locator(locator).first
            if element.count() > 0 and element.is_visible():
                field_input = TextInput(self.page, element, widget_name)
                self.content_items[widget_name] = field_input
                logger.debug(f"Initialized text field: '{field_label}'")
        except Exception as e:
            logger.error(f"Error initializing text field '{field_label}': {e}")

    def _init_combobox_fields(self) -> None:
        """Инициализирует combobox поля формы."""
        for field_label, (attr_name, input_name, list_name) in self.COMBOBOX_FIELDS_MAPPING.items():
            locator = self.COMBOBOX_FIELDS_LOCATORS.get(field_label)
            if not locator:
                continue
            self._init_single_combobox_field(field_label, locator, input_name, list_name)

    def _init_single_combobox_field(
        self, field_label: str, locator: str, input_name: str, list_name: str
    ) -> None:
        """Инициализирует одно combobox поле."""
        try:
            element = self.page.locator(locator).first
            if element.count() > 0 and element.is_visible():
                field_input = TextInput(self.page, element, input_name)
                self.content_items[input_name] = field_input
                self.content_items[list_name] = DropdownList(self.page)
                logger.debug(f"Initialized combobox field: '{field_label}'")
        except Exception as e:
            logger.error(f"Error initializing combobox field '{field_label}': {e}")

    def _init_checkbox_fields(self) -> None:
        """Инициализирует checkbox поля формы (опционально)."""
        if not self.CHECKBOX_FIELDS_MAPPING:
            return

        for field_label, (attr_name, widget_name) in self.CHECKBOX_FIELDS_MAPPING.items():
            locator = self.CHECKBOX_FIELDS_LOCATORS.get(field_label)
            if not locator:
                continue
            self._init_single_checkbox_field(field_label, locator, widget_name)

    def _init_single_checkbox_field(self, field_label: str, locator: str, widget_name: str) -> None:
        """Инициализирует одно checkbox поле."""
        try:
            checkbox_input = self.page.locator(locator).first
            if checkbox_input.count() == 0:
                logger.debug(f"Checkbox '{field_label}' not found")
                return

            # Импортируем здесь чтобы избежать циклических импортов
            from elements.checkbox_element import Checkbox

            checkbox = Checkbox(self.page, checkbox_input, widget_name)
            self.content_items[widget_name] = checkbox
            logger.debug(f"Initialized checkbox field: '{field_label}'")
        except Exception as e:
            logger.error(f"Error initializing checkbox '{field_label}': {e}")

    def get_content_item(self, item_name: str) -> Any:
        """Возвращает элемент контента по имени."""
        return self.content_items.get(item_name)

    def clear_field(self, field_name: str) -> None:
        """Очищает указанное поле."""
        logger.debug(f"Clearing field: '{field_name}'")

        # Проверяем, не является ли поле чекбоксом
        if field_name in self.CHECKBOX_FIELDS_LOCATORS:
            logger.debug(f"Field '{field_name}' is a checkbox, skipping clear operation")
            return

        # Получаем локатор поля
        locator = self._get_field_locator(field_name)
        if not locator:
            logger.warning(f"Unknown field: {field_name}")
            return

        field_element = self.page.locator(locator).first
        if field_element.count() == 0:
            logger.debug(f"Field '{field_name}' not found")
            return

        # Очистка в зависимости от типа поля
        if field_name in self.TEXT_FIELDS_LOCATORS:
            self._clear_text_field(field_element, field_name)
        elif field_name in self.COMBOBOX_FIELDS_LOCATORS:
            self._clear_combobox_field(field_element, field_name)

    def _get_field_locator(self, field_name: str) -> Optional[str]:
        """Получает локатор поля по его названию."""
        if field_name in self.COMBOBOX_FIELDS_LOCATORS:
            return self.COMBOBOX_FIELDS_LOCATORS[field_name]
        elif field_name in self.TEXT_FIELDS_LOCATORS:
            return self.TEXT_FIELDS_LOCATORS[field_name]
        elif field_name in self.CHECKBOX_FIELDS_LOCATORS:
            return self.CHECKBOX_FIELDS_LOCATORS[field_name]
        return None

    def _clear_text_field(self, field_element, field_name: str) -> None:
        """Очищает текстовое поле."""
        try:
            field_element.click()
            field_element.page.keyboard.press("Control+A")
            field_element.page.keyboard.press("Backspace")
            self.wait_for_timeout(200)
            logger.debug(f"Text field '{field_name}' cleared")
        except Exception as e:
            logger.debug(f"Could not clear text field '{field_name}': {e}")

    def _clear_combobox_field(self, field_element, field_name: str) -> None:
        """Очищает combobox поле."""
        try:
            parent_container = field_element.locator(
                "xpath=ancestor::div[contains(@class, 'v-input')]"
            ).first

            if parent_container.count() == 0:
                logger.debug(f"Parent container not found for field '{field_name}'")
                return

            clear_button = parent_container.locator(
                ".v-input__icon--clear button, .v-input__icon--append button, i.mdi-close-circle, i.mdi-close"
            ).first

            if clear_button.count() > 0 and clear_button.is_visible():
                clear_button.click()
                self.wait_for_timeout(300)
                logger.debug(f"Combobox field '{field_name}' cleared")
            else:
                logger.debug(f"Clear button not found for field '{field_name}'")
        except Exception as e:
            logger.debug(f"Error clearing combobox field '{field_name}': {e}")

    def _scroll_to_element_in_dropdown(self, value: str) -> bool:
        """Скроллит выпадающий список до элемента с нужным текстом."""
        logger.debug(f"Scrolling to find element with text: '{value}'")

        dropdown_menu = self.page.locator("div.menuable__content__active").first
        if dropdown_menu.count() == 0:
            logger.error("Active dropdown menu not found")
            return False

        max_attempts = 10
        attempts = 0

        while attempts < max_attempts:
            visible_items = dropdown_menu.locator("a.v-list__tile, div[role='listitem']").all()

            if visible_items:
                for item in visible_items:
                    item_text = item.text_content() or ""
                    if value in item_text:
                        logger.debug(f"Found element with text '{value}'")
                        item.scroll_into_view_if_needed()
                        self.wait_for_timeout(300)
                        return True

                last_item = visible_items[-1]
                last_item_text = last_item.text_content() or ""
                logger.debug(f"Scrolling to last visible item: '{last_item_text}'")
                last_item.scroll_into_view_if_needed()
                self.wait_for_timeout(500)
            else:
                dropdown_menu.evaluate("(el) => el.scrollTop += 200")
                self.wait_for_timeout(300)

            attempts += 1

        logger.warning(f"Element with text '{value}' not found after {max_attempts} attempts")
        return False

    def _fill_text_fields(self, rack_data: BaseRackData, results: Dict[str, int]) -> None:
        """Заполняет текстовые поля."""
        for field_label, (attr_name, field_name) in self.TEXT_FIELDS_MAPPING.items():
            value = getattr(rack_data, attr_name, "")
            if not value or not str(value).strip():
                continue
            self._fill_single_text_field(field_label, field_name, value, results)

    def _fill_single_text_field(
        self, field_label: str, field_name: str, value: str, results: Dict[str, int]
    ) -> None:
        """Заполняет одно текстовое поле."""
        try:
            input_field = self.get_content_item(field_name)
            if input_field:
                input_field.input_value(value)
                results["text_fields_filled"] += 1
                logger.debug(f"Field '{field_label}' filled: '{value}'")
        except Exception as e:
            logger.error(f"Error filling field '{field_label}': {e}")

    def _fill_combobox_fields(self, rack_data: BaseRackData, results: Dict[str, int]) -> None:
        """Заполняет combobox поля."""
        for field_label, (attr_name, input_name, list_name) in self.COMBOBOX_FIELDS_MAPPING.items():
            value = getattr(rack_data, attr_name, "")
            if not value or not str(value).strip():
                continue
            self._fill_single_combobox_field(field_label, input_name, list_name, value, results)

    def _fill_single_combobox_field(
        self, field_label: str, input_name: str, list_name: str, value: str, results: Dict[str, int]
    ) -> None:
        """Заполняет одно combobox поле."""
        try:
            combobox_field = self.get_content_item(input_name)
            if not combobox_field:
                logger.warning(f"Field '{field_label}' input not found")
                return

            combobox_field.click(force=True)
            self.wait_for_timeout(1000)

            if not self._scroll_to_element_in_dropdown(value):
                logger.error(f"Could not find element with text '{value}' after scrolling")
                self.page.mouse.click(10, 10)
                self.wait_for_timeout(300)
                return

            dropdown_menu = self.page.locator("div.menuable__content__active").first
            item_locator = self._find_dropdown_item(dropdown_menu, value)

            if item_locator and item_locator.count() > 0:
                item_locator.scroll_into_view_if_needed()
                self.wait_for_timeout(300)
                item_locator.click()
                results["combobox_fields_filled"] += 1
                logger.debug(f"Field '{field_label}' set: '{value}'")
                self.wait_for_timeout(500)
            else:
                logger.error(f"Item with text '{value}' not found in dropdown for field '{field_label}'")
                self.page.mouse.click(10, 10)
                self.wait_for_timeout(300)

        except Exception as e:
            logger.error(f"Error filling combobox '{field_label}': {e}")
            self.page.mouse.click(10, 10)

    def _find_dropdown_item(self, dropdown_menu, value: str):
        """Находит элемент в выпадающем списке."""
        item_locator = dropdown_menu.locator(f"a.v-list__tile:has-text('{value}')").first
        if item_locator.count() == 0:
            item_locator = dropdown_menu.locator(f"span:has-text('{value}')").first
        if item_locator.count() == 0:
            item_locator = dropdown_menu.locator(f"div[role='listitem']:has-text('{value}')").first
        return item_locator

    def _fill_checkbox_fields(self, rack_data: BaseRackData, results: Dict[str, int]) -> None:
        """Заполняет checkbox поля (опционально)."""
        if not hasattr(self, 'CHECKBOX_FIELDS_MAPPING'):
            return

        for field_label, (attr_name, widget_name) in self.CHECKBOX_FIELDS_MAPPING.items():
            value = getattr(rack_data, attr_name, None)
            if value is None:
                continue
            self._fill_single_checkbox_field(field_label, widget_name, value, results)

    def _fill_single_checkbox_field(
        self, field_label: str, widget_name: str, value: bool, results: Dict[str, int]
    ) -> None:
        """Заполняет одно checkbox поле."""
        try:
            checkbox = self.get_content_item(widget_name)
            if not checkbox:
                logger.warning(f"Checkbox '{field_label}' not found")
                return

            if value:
                checkbox.check(force=True)
                logger.debug(f"Checkbox '{field_label}' checked")
            else:
                checkbox.uncheck(force=True)
                logger.debug(f"Checkbox '{field_label}' unchecked")

            results["checkboxes_set"] += 1

        except Exception as e:
            logger.error(f"Error setting checkbox '{field_label}': {e}")

    @abstractmethod
    def fill_rack_data(self, rack_data: BaseRackData) -> Dict[str, int]:
        """Абстрактный метод для заполнения данных стойки."""
        pass

    def is_field_highlighted_as_error(self, field_name: str) -> bool:
        """Проверяет, подсвечено ли поле как ошибочное."""
        # Для чекбоксов не проверяем ошибки
        if field_name in self.CHECKBOX_FIELDS_LOCATORS:
            return False

        locator = self._get_field_locator(field_name)
        if not locator:
            return False

        field_element = self.page.locator(locator).first
        if field_element.count() == 0:
            logger.debug(f"Field '{field_name}' not found")
            return False

        parent_input = field_element.locator(
            "xpath=ancestor::div[contains(@class, 'v-input')]"
        ).first

        if parent_input.count() > 0:
            class_attr = parent_input.get_attribute("class") or ""
            is_error = "v-input--error" in class_attr or "error--text" in class_attr
            logger.debug(f"Field '{field_name}' error state: {is_error}")
            return is_error

        return False

    def verify_required_fields_highlighted(self, field_names: List[str]) -> Dict[str, bool]:
        """Проверяет, что указанные поля подсвечены как обязательные."""
        results = {}
        for field_name in field_names:
            results[field_name] = self.is_field_highlighted_as_error(field_name)
            logger.debug(f"Field '{field_name}' highlighted: {results[field_name]}")
        return results

    def wait_for_field_error(self, field_name: str, timeout: int = 5000) -> bool:
        """Ожидает появления подсветки ошибки на поле."""
        if field_name in self.CHECKBOX_FIELDS_LOCATORS:
            return False

        start_time = time.time()
        while (time.time() - start_time) * 1000 < timeout:
            if self.is_field_highlighted_as_error(field_name):
                return True
            self.wait_for_timeout(200)
        return False

    def get_field_value(self, field_name: str) -> Optional[str]:
        """Получает значение поля."""
        # Для чекбоксов
        if field_name in self.CHECKBOX_FIELDS_LOCATORS:
            for field_label, (attr_name, widget_name) in self.CHECKBOX_FIELDS_MAPPING.items():
                if attr_name == field_name or field_label == field_name:
                    checkbox = self.get_content_item(widget_name)
                    if checkbox:
                        return str(checkbox.is_checked())
            return None

        # Для текстовых полей
        if field_name in self.TEXT_FIELDS_LOCATORS:
            for field_label, (attr_name, widget_name) in self.TEXT_FIELDS_MAPPING.items():
                if attr_name == field_name or field_label == field_name:
                    input_field = self.get_content_item(widget_name)
                    if input_field:
                        return input_field.get_input_value()
            return None

        # Для combobox полей
        return self._get_combobox_value(field_name)

    def _get_combobox_value(self, field_name: str) -> Optional[str]:
        """Получает значение combobox поля."""
        locator = self.COMBOBOX_FIELDS_LOCATORS.get(field_name)
        if not locator:
            for field_label, (attr_name, input_name, _) in self.COMBOBOX_FIELDS_MAPPING.items():
                if attr_name == field_name or field_label == field_name:
                    input_field = self.get_content_item(input_name)
                    if input_field:
                        selections = input_field.element.locator(
                            "xpath=ancestor::div[contains(@class, 'v-select__selections')]"
                        ).first
                        if selections.count() > 0:
                            value_span = selections.locator("span").first
                            return value_span.text_content() or ""
            return None

        element = self.page.locator(locator).first
        if element.count() > 0:
            selections = element.locator(
                "xpath=ancestor::div[contains(@class, 'v-select__selections')]"
            ).first
            if selections.count() > 0:
                value_span = selections.locator("span").first
                return value_span.text_content() or ""
        return None
