"""Модуль тестов редактирования стойки в модуле Объекты.

Содержит тесты для проверки функциональности
редактирования стойки оборудования.
"""

import os
import pytest
from playwright.sync_api import Page
from tools.logger import get_logger
from locators.navigation_panel_locators import NavigationPanelLocators
from frames.create_element_frame import CreateElementFrame
from forms.create_rack_form import CreateRackForm, CreateRackData
from makers.edit_rack_maker import EditRackMaker, EditRackData
from pages.location_page import LocationPage
from pages.login_page import LoginPage
from pages.main_page import MainPage
from pages.rack_page import RackPage
from components.alert_component import AlertComponent


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


class TestRackEdit:
    """Набор тестов для редактирования стойки в модуле Объекты.

    Проверяет функциональность редактирования различных вкладок стойки:
    1. Общая информация
    2. Изображение
    3. Правила доступа
    """

    # Имя тестовой стойки
    RACK_NAME = "Test-Rack-Edit"

    # Инициализируем атрибуты
    main_page: MainPage = None
    location_page: LocationPage = None
    alert: AlertComponent = None
    create_child_frame: CreateElementFrame = None

    @pytest.fixture(scope="function", autouse=True)
    def setup(self, browser: Page) -> None:
        """Фикстура для подготовки тестового окружения.

        Выполняет:
        1. Авторизацию в системе
        2. Переход к локации test-zone
        3. Инициализацию компонентов
        4. Создание стойки если она не существует
        5. Переход к стойке

        Args:
            browser: Экземпляр страницы Playwright для взаимодействия с UI
        """

        # Авторизация в системе
        login_page = LoginPage(browser)
        login_page.do_login()

        # Мы на главной странице
        self.main_page = MainPage(browser)
        self.main_page.should_be_navigation_panel()

        # Переходим к Объектам
        self.main_page.click_main_navigation_panel_item("Объекты")
        self.main_page.wait_for_timeout(1000)
        self.main_page.click_main_navigation_panel_item("test-zone")

        # Создаем экземпляр страницы локации
        self.location_page = LocationPage(browser)

        # Инициализируем компонент алертов (вынесено в атрибуты класса)
        self.alert = AlertComponent(browser)

        # Инициализируем фрейм создания дочернего элемента (вынесено в атрибуты класса)
        self.create_child_frame = CreateElementFrame(browser)

        # Проверяем существование стойки
        if not self._check_rack_existance(browser, self.RACK_NAME):
            logger.info(f"Rack '{self.RACK_NAME}' does not exist. Creating...")

            rack_data = CreateRackData(
                name=self.RACK_NAME,
                usize="42",
                depth="1000"
            )
            self._create_rack(browser, rack_data)
            self.main_page.wait_for_timeout(3000)
        else:
            logger.info(f"Rack '{self.RACK_NAME}' already exists")

        # Переходим к стойке для тестирования
        self.main_page.click_subpanel_item(self.RACK_NAME, parent="test-zone")
        self.main_page.wait_for_timeout(3000)

    @pytest.fixture(scope="class", autouse=True)
    def cleanup_rack(self, browser: Page):
        """Фикстура для очистки созданной стойки после ВСЕХ тестов класса.

        Выполняется один раз после завершения всех тестов класса TestRackEdit.
        Удаляет созданную стойку.

        Args:
            browser: Экземпляр страницы Playwright
        """

        # Тесты выполняются здесь
        yield

        logger.debug(f"Cleaning up rack: {self.RACK_NAME}")

        # Переходим на главную страницу и в нужную зону
        login_page = LoginPage(browser)
        login_page.do_login()

        self.main_page = MainPage(browser)
        self.main_page.should_be_navigation_panel()

        # Переходим к Объектам
        self.main_page.click_main_navigation_panel_item("Объекты")
        self.main_page.wait_for_timeout(1000)
        self.main_page.click_main_navigation_panel_item("test-zone")
        self.main_page.wait_for_timeout(1000)

        # Инициализируем компонент алертов для cleanup
        self.alert = AlertComponent(browser)

        # Проверяем существование стойки
        if self._check_rack_existance(browser, self.RACK_NAME):

            # Переходим на страницу стойки
            self.main_page.click_subpanel_item(self.RACK_NAME, parent="test-zone")
            self.main_page.wait_for_timeout(2000)

            # Удаляем стойку
            self._delete_rack(browser, self.RACK_NAME)

            # Дополнительная проверка
            self.main_page.click_subpanel_item("test-zone")
            self.main_page.wait_for_timeout(1000)

    def _check_rack_existance(self, browser: Page, rack_name: str) -> bool:
        """Проверяет существование стойки.

        Args:
            browser: Страница Playwright
            rack_name: Имя стойки для проверки

        Returns:
            bool: True если стойка существует, False в противном случае
        """

        logger.debug(f"Checking existence of rack with name '{rack_name}'")

        self.main_page.click_subpanel_item("test-zone")

        nav_panel_locator = NavigationPanelLocators.TREEVIEW
        element = browser.locator(nav_panel_locator).get_by_text(rack_name, exact=True).first

        if element.is_visible():
            logger.debug(f"Rack with name '{rack_name}' found")
            return True

        logger.debug(f"Rack with name '{rack_name}' not found")
        return False

    def _create_rack(self, browser: Page, rack_data: CreateRackData) -> None:
        """Создает стойку.

        Args:
            browser: Страница Playwright
            rack_data: Данные стойки для создания
        """

        logger.debug(f"Creating rack with name '{rack_data.name}'")

        # Нажимаем кнопку "Создать" на тулбаре
        self.location_page.click_create_button()

        # Нажимаем на плашку "Класс объекта учета"
        self.create_child_frame.open_object_class_combobox()

        # Из выпадающего меню выбираем пункт "Стойка"
        self.create_child_frame.select_object_class("Стойка")

        # Создаем форму создания стойки
        rack_form = CreateRackForm(browser)

        # Заполняем данные стойки
        fill_results = rack_form.fill_rack_data(rack_data)
        logger.debug(f"Fill results: {fill_results}")

        # Нажимаем кнопку создания
        self.create_child_frame.click_add_button()

        # Ждем появления alert с текстом
        expected_alert_text = f"Успешно создано"
        self.alert.check_alert_presence(expected_alert_text, timeout=7000)

        self.alert.check_alert_absence(expected_alert_text, timeout=7000)

        # Закрываем alert с текстом кнопкой 'Закрыть'
        try:
            self.alert.close_alert_by_text(expected_alert_text)
            logger.debug("Alert forcibly closed")
        except AssertionError:
            # Если уже закрылся - игнорируем
            logger.debug("Alert already closed by the time forcible close was attempted")

        logger.info(f"Rack '{rack_data.name}' created successfully")

    def _delete_rack(self, browser: Page, rack_name: str) -> None:
        """Удаляет стойку через контекстное меню.

        Args:
            browser: Страница Playwright
            rack_name: Имя стойки для удаления
        """

        # Находим элемент стойки в навигационной панели
        rack_element = browser.locator(
            NavigationPanelLocators.TREEVIEW
        ).get_by_text(rack_name, exact=True).first

        # Прокручиваем до элемента если нужно
        rack_element.scroll_into_view_if_needed()
        self.main_page.wait_for_timeout(500)

        # Проверяем и нажимаем кнопку "Изменить"
        rack_page = RackPage(browser)

        # Проверяем видимость и тултип кнопки
        rack_page.should_be_toolbar_buttons()

        # Кликаем на кнопку "Изменить"
        rack_page.click_edit_button()
        self.main_page.wait_for_timeout(1000)

        # Создаем экземпляр EditRackMaker
        rack_edit = EditRackMaker(browser, rack_name)

        # Используем метод для удаления
        rack_edit.click_remove_button()

        # Проверяем уведомление об успешном удалении
        expected_alert_text = "Успешно удалено"
        self.alert.check_alert_presence(expected_alert_text, timeout=7000)

        self.alert.check_alert_absence(expected_alert_text, timeout=7000)

        # Закрываем alert с текстом кнопкой 'Закрыть'
        try:
            self.alert.close_alert_by_text(expected_alert_text)
            logger.debug("Alert forcibly closed")
        except AssertionError:
            # Если уже закрылся - игнорируем
            logger.debug("Alert already closed by the time forcible close was attempted")

        logger.info(f"Rack '{rack_name}' deleted successfully")

    def test_rack_general_info_tab_fields(self, browser: Page) -> None:
        """Тест заполнения полей вкладки 'Общая информация' стойки."""

        logger.debug(f"Starting general info tab test for rack: {self.RACK_NAME}")

        rack_page = RackPage(browser)

        # Переходим в режим редактирования
        rack_page.click_edit_button()
        rack_page.wait_for_timeout(1000)

        # Создаем экземпляр EditRackMaker
        rack_edit = EditRackMaker(browser, self.RACK_NAME)

        # Создаем тестовые данные для заполнения всех полей
        rack_edit_data = EditRackData(
            # Основные поля
            name=self.RACK_NAME,
            serial="SN123456789",
            inventory="INV987654321",
            comment="Тестовый комментарий для стойки (обновленный)",
            allocated_power="5500",

            # Combobox поля
            cable_entry="сверху",
            state="Введен в эксплуатацию",
            owner="",
            service_org="",
            project="",

            # Checkbox поля
            ventilation_panel=True,
        )

        # Заполняем все поля формы
        results = rack_edit.fill_rack_data(rack_edit_data)

        logger.debug(f"Fill results: {results}")

        # Сохраняем изменения
        rack_edit.click_done_button()

        # Проверяем уведомление об успешном обновлении
        expected_alert_text = "Успешно обновлено"
        self.alert.check_alert_presence(expected_alert_text, timeout=7000)

        self.alert.check_alert_absence(expected_alert_text, timeout=7000)

        # Закрываем alert с текстом кнопкой 'Закрыть'
        try:
            self.alert.close_alert_by_text(expected_alert_text)
            logger.debug("Alert forcibly closed")
        except AssertionError:
            logger.debug("Alert already closed by the time forcible close was attempted")

        # Вход в режим редактирования
        rack_page.click_edit_button()

        # Проверяем поля, пропуская недоступные
        verification_results = rack_edit.verify_all_filled_fields(
            rack_edit_data,
            skip_fields=["Владелец", "Обслуживающая организация", "Проект/Титул"]
        )
        logger.debug(f"Verification results: {verification_results}")

        # Проверяем результаты
        assert verification_results["incorrectly_filled"] == 0, \
            f"Available fields incorrectly filled: {verification_results['field_errors']}"
        assert verification_results["not_filled"] == 0, \
            f"Available fields not filled: {verification_results['field_errors']}"

        rack_edit.click_close_button()

        logger.debug("General info tab test completed successfully")

    def test_required_field_name_validation(self, browser: Page) -> None:
        """Тест проверки обязательного поля ИМЯ при создании стойки."""

        logger.debug(f"Starting required field name validation test for rack: {self.RACK_NAME}")

        rack_page = RackPage(browser)

        # Переходим в режим редактирования
        rack_page.click_edit_button()

        # Создаем экземпляр EditRackMaker
        rack_edit = EditRackMaker(browser, self.RACK_NAME)

        # ========== Тест 1: Обязательное поле имя пустое ==========
        # Очищаем поле имя
        rack_edit.clear_field("Имя")

        # Создаем тестовые данные для заполнения поля
        test_data_1 = EditRackData(
            name=""
        )

        rack_edit.fill_rack_data(test_data_1)
        rack_edit.click_done_button()

        # Проверяем уведомление об ошибочном обновлении
        expected_alert_text = "поле ИМЯ должно быть заполнено, и не должно превышать 35 знаков"
        self.alert.check_alert_presence(expected_alert_text, timeout=7000)
        self.alert.check_alert_absence(expected_alert_text, timeout=7000)

        # ========== Тест 2: Обязательное поле имя не должно превышать 35 знаков ==========
        # Создаем тестовые данные для заполнения поля
        test_data_2 = EditRackData(
            name="_123456789_123456789_123456789_12345"
        )

        rack_edit.fill_rack_data(test_data_2)
        rack_edit.click_done_button()

        # Проверяем уведомление об ошибочном обновлении
        expected_alert_text = "поле ИМЯ должно быть заполнено, и не должно превышать 35 знаков"
        self.alert.check_alert_presence(expected_alert_text, timeout=7000)
        self.alert.check_alert_absence(expected_alert_text, timeout=7000)

        rack_edit.click_close_button()

        logger.debug("Required field name validation test completed successfully")

    def test_rack_image_tab(self, browser: Page) -> None:
        """Тест вкладки 'Изображение' стойки."""

        logger.debug(f"Starting image tab test for rack: {self.RACK_NAME}")

        rack_page = RackPage(browser)

        # Переходим в режим редактирования
        rack_page.click_edit_button()
        rack_page.wait_for_timeout(1000)

        # Создаем экземпляр EditRackMaker
        rack_edit = EditRackMaker(browser, self.RACK_NAME)

        # Переключаемся на вкладку "Изображение"
        rack_edit.switch_to_tab(EditRackMaker.TAB_IMAGE)

        # Проверяем вкладку
        assert rack_edit.is_tab_active(EditRackMaker.TAB_IMAGE), "Image tab should be active"

        # Загружаем изображение если есть
        test_image_path = os.path.join(os.path.dirname(__file__), "test_edit_rack_image.jpg")
        if os.path.exists(test_image_path):
            logger.debug(f"Found test image: {test_image_path}")
            # Находим input и загружаем файл
            file_input = browser.locator("input[type='file']")
            if file_input.count() == 0:
                file_input = browser.locator(".button-file-upload__input")

            if file_input.count() > 0:
                file_input.set_input_files(test_image_path)
                rack_page.wait_for_timeout(2000)
                logger.debug("Test image uploaded")
        else:
            logger.warning(f"Test image not found at: {test_image_path}")

        # Сохраняем
        rack_edit.click_done_button()

        # Проверяем уведомление об успешном обновлении
        expected_alert_text = "Успешно обновлено"
        self.alert.check_alert_presence(expected_alert_text, timeout=7000)

        self.alert.check_alert_absence(expected_alert_text, timeout=7000)

        # Закрываем alert с текстом кнопкой 'Закрыть'
        try:
            self.alert.close_alert_by_text(expected_alert_text)
            logger.debug("Alert forcibly closed")
        except AssertionError:
            logger.debug("Alert already closed by the time forcible close was attempted")

        logger.debug("Image tab test completed successfully")

    def test_rack_access_rules(self, browser: Page) -> None:
        """Тест заполнения полей правил доступа.

        В каждое поле добавляются ВСЕ пользователи из списка custom_users.
        """

        logger.debug(f"Starting access rules test for rack: {self.RACK_NAME}")

        rack_page = RackPage(browser)

        # Переходим в режим редактирования
        rack_page.click_edit_button()
        rack_page.wait_for_timeout(1000)

        # Создаем экземпляр EditRackMaker
        rack_edit = EditRackMaker(browser, self.RACK_NAME)

        # Переключаемся на вкладку "Настройки"
        rack_edit.switch_to_tab(EditRackMaker.TAB_SETTINGS)

        # Проверяем, что вкладка активна
        assert rack_edit.is_tab_active(EditRackMaker.TAB_SETTINGS), \
            "Settings tab should be active after switching"

        # Целевые поля для заполнения
        target_fields = [
            "read_access_rules",
            "write_access_rules",
            "sms_access_rules",
            "email_access_rules",
            "push_access_rules"
        ]

        # Пользователи для добавления в каждое поле
        custom_users = [
            "admin",
            "manager",
            "operator",
            "sec",
            "collector"
        ]

        # Заполняем поля
        fill_results = rack_edit.fill_access_rules(
            users_to_add=custom_users,
            target_fields=target_fields
        )

        # Проверяем, что все пользователи были добавлены
        expected_total = len(target_fields) * len(custom_users)
        assert fill_results["access_rules_filled"] == expected_total, \
            f"Added {fill_results['access_rules_filled']} users, expected {expected_total}"
        assert len(fill_results["errors"]) == 0, \
            f"Errors during filling: {fill_results['errors']}"

        # Проверяем заполнение
        verification_results = rack_edit.verify_access_rules(
            expected_users=custom_users,
            target_fields=target_fields
        )
        logger.debug(f"Verification results: {verification_results}")

        # Проверяем результаты
        assert verification_results["correctly_filled"] == expected_total, \
            f"Correctly filled {verification_results['correctly_filled']} out of {expected_total}"
        assert verification_results["incorrectly_filled"] == 0, \
            f"Verification errors: {verification_results['field_errors']}"

        # Дополнительная проверка
        assert len(verification_results["fields_verified"]) == len(target_fields), \
            f"Fields verified: {len(verification_results['fields_verified'])}, expected: {len(target_fields)}"

        # Сохраняем изменения
        rack_edit.click_done_button()

        # Проверяем уведомление об успешном обновлении
        expected_alert_text = "Успешно обновлено"
        self.alert.check_alert_presence(expected_alert_text, timeout=7000)

        self.alert.check_alert_absence(expected_alert_text, timeout=7000)

        # Закрываем alert с текстом кнопкой 'Закрыть'
        try:
            self.alert.close_alert_by_text(expected_alert_text)
            logger.debug("Alert forcibly closed")
        except AssertionError:
            logger.debug("Alert already closed by the time forcible close was attempted")

        # Возвращаемся в режим редактирования и проверяем снова
        rack_page.click_edit_button()
        rack_page.wait_for_timeout(1000)

        rack_edit = EditRackMaker(browser, self.RACK_NAME)
        rack_edit.switch_to_tab(EditRackMaker.TAB_SETTINGS)

        verification_results_after_save = rack_edit.verify_access_rules(
            expected_users=custom_users,
            target_fields=target_fields
        )
        logger.debug(f"Verification results after save: {verification_results_after_save}")

        assert verification_results_after_save["correctly_filled"] == expected_total, \
            f"After save - correctly filled {verification_results_after_save['correctly_filled']} out of {expected_total}"

        rack_edit.click_close_button()

        logger.debug("Access rules test completed successfully")
