"""Базовый класс страницы для работы с Playwright.

Содержит общие методы для взаимодействия со страницей и API.
"""

from playwright.sync_api import Page, Response, APIRequestContext, expect
from data.environment import host
from tools.logger import get_logger
import json

logger = get_logger("BASE_PAGE")


class BasePage:
    """Базовый класс для работы со страницами через Playwright.

    Атрибуты:
        page (Page): Экземпляр страницы Playwright.
    """

    def __init__(self, page: Page):
        """Инициализирует базовую страницу.

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

    # Действия:
    def current_url(self) -> str:
        """Возвращает текущий URL страницы.

        Returns:
            str: Текущий URL страницы.
        """
        return self.page.url

    def open(self, uri) -> Response | None:
        """Открывает указанный URI в браузере.

        Args:
            uri (str): URI для открытия (без базового URL).

        Returns:
            Response | None: Ответ сервера или None в случае ошибки.
        """
        return self.page.goto(f"{host.get_base_url()}{uri}", wait_until='domcontentloaded')

    def page_reload(self) -> None:
        """Перезагружает текущую страницу."""
        self.page.reload()

    def wait_for_timeout(self, timeout):
        """Ожидает указанное количество миллисекунд.

        Args:
            timeout (int): Время ожидания в миллисекундах.
        """
        self.page.wait_for_timeout(timeout)

    def get_api_request_context(self) -> APIRequestContext:
        """Возвращает контекст API-запросов.

        Returns:
            APIRequestContext: Контекст для выполнения API-запросов.
        """
        return self.page.context.request

    def send_get_api_request(self, uri) -> Response:
        """Отправляет GET-запрос к API.

        Args:
            uri (str): URI API-эндпоинта (без базового URL).

        Returns:
            Response: Ответ сервера.
        """
        api_request_context = self.get_api_request_context()
        token = host.get_access_token()
        headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
        response = api_request_context.get(
            f"{host.get_request_url()}{uri}",
            headers=headers
        )
        return response

    def send_post_api_request(self, uri, payload) -> Response:
        """Отправляет POST-запрос к API.

        Args:
            uri (str): URI API-эндпоинта (без базового URL).
            payload: Данные для отправки в теле запроса.

        Returns:
            Response: Ответ сервера.
        """
        api_request_context = self.get_api_request_context()
        token = host.get_access_token()
        headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
        response = api_request_context.post(
            f"{host.get_request_url()}{uri}",
            headers=headers,
            data=payload
        )
        return response

    def get_response_body(self, response) -> dict | None:
        """Извлекает тело ответа в формате JSON.

        Args:
            response (Response): Ответ сервера.

        Returns:
            dict | None: Распарсенное тело ответа или None в случае ошибки.
        """
        try:
            response_body = response.json()
        except json.JSONDecodeError:
            logger.error("Failed to decode JSON response")
            return None
        return response_body

    # Проверки:
    def check_URL(self, uri: str, msg: str) -> None:
        """Проверяет, что текущий URL соответствует ожидаемому.

        Args:
            uri (str): Ожидаемый URI (без базового URL).
            msg (str): Сообщение об ошибке при несоответствии.

        Raises:
            AssertionError: Если URL не соответствует ожидаемому.
        """
        expect(self.page).to_have_url(
            f"{host.get_base_url()}{uri}",
            timeout=60000
        ), msg

    def check_equals(self, actual, expected, msg: str) -> None:
        """Проверяет равенство фактического и ожидаемого значений.

        Args:
            actual: Фактическое значение.
            expected: Ожидаемое значение.
            msg (str): Сообщение об ошибке при несоответствии.

        Raises:
            AssertionError: Если значения не равны.
        """
        assert actual == expected, msg

    def check_lists_equals(self, actual: list, expected: list, msg: str) -> None:
        """Рекурсивно проверяет равенство двух списков.

        Args:
            actual (list): Фактический список.
            expected (list): Ожидаемый список.
            msg (str): Сообщение об ошибке при несоответствии.

        Raises:
            AssertionError: Если списки не равны.
        """
        def compare_lists(list1: list, list2: list) -> bool:
            """Вспомогательная функция для рекурсивного сравнения списков.

            Args:
                list1 (list): Первый список для сравнения.
                list2 (list): Второй список для сравнения.

            Returns:
                bool: True если списки идентичны, иначе False.
            """
            if len(list1) != len(list2):
                return False
            for item1, item2 in zip(list1, list2):
                if isinstance(item1, list) and isinstance(item2, list):
                    if not compare_lists(item1, item2):
                        return False
                elif item1 != item2:
                    return False
            return True

        assert compare_lists(actual, expected), msg