"""Refactored API class with improved architecture."""
import asyncio
import logging
from typing import List, Optional

from ..client.aiohttp import AiohttpClient
from ..types.models import File, Order
from ..const import Get, Post, USER_AGENT, MAX_CONCURRENT_REQUESTS
from ..exceptions import AuthenticationError, SessionExpiredError, NetworkError
from ..parsers import OrderParser
from ..utils import async_retry, RateLimiter

logger = logging.getLogger(__name__)


class API:
    """
    Refactored API для работы с 4writers.net.

    Улучшения:
    - Context manager для автоматического закрытия ресурсов
    - Rate limiting для предотвращения блокировок
    - Retry логика для сетевых ошибок
    - Разделение ответственности (парсинг вынесен в OrderParser)
    - Улучшенная обработка ошибок
    """

    def __init__(
        self,
        login: Optional[str] = None,
        password: Optional[str] = None,
        session: Optional[str] = None,
        max_concurrent_requests: int = MAX_CONCURRENT_REQUESTS,
    ):
        """
        Args:
            login: Логин для авторизации
            password: Пароль для авторизации
            session: Готовая сессия (если уже авторизованы)
            max_concurrent_requests: Максимум параллельных запросов
        """
        self._login = login
        self._password = password
        self._session = session
        self.http_client = AiohttpClient()
        self.parser = OrderParser()
        self.rate_limiter = RateLimiter(max_concurrent=max_concurrent_requests)
        self._is_authenticated = session is not None

    async def __aenter__(self) -> "API":
        """Context manager entry."""
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
        """Context manager exit - автоматически закрывает ресурсы."""
        await self.close()

    async def close(self) -> None:
        """Закрывает HTTP клиент и освобождает ресурсы."""
        await self.http_client.close()

    @async_retry(max_attempts=3, delay=1.0, exceptions=(NetworkError,))
    async def login(self) -> None:
        """
        Авторизация через login и password.

        Raises:
            AuthenticationError: Если авторизация не удалась
            NetworkError: Если произошла ошибка сети
        """
        if not self._login or not self._password:
            raise AuthenticationError("Login and password are required for authentication.")

        try:
            response = await self.http_client.request_raw(
                url=Post.LOGIN,
                method="POST",
                headers={
                    "User-Agent": USER_AGENT,
                    "Referer": Post.LOGIN,
                },
                data={"login": self._login, "password": self._password},
            )

            if response.status != 200:
                raise AuthenticationError(f"Login failed with status {response.status}")

            session_cookie = response.cookies.get("session")
            if session_cookie:
                self._session = session_cookie.value
                self._is_authenticated = True
                logger.info("Authentication successful")
            else:
                raise AuthenticationError("Session ID not found in response")

        except Exception as e:
            if isinstance(e, AuthenticationError):
                raise
            raise NetworkError(f"Network error during login: {e}") from e

    def _get_auth_headers(self, extra_headers: Optional[dict] = None) -> dict:
        """
        Возвращает заголовки с авторизацией.

        Args:
            extra_headers: Дополнительные заголовки

        Returns:
            Словарь заголовков
        """
        headers = {
            "User-Agent": USER_AGENT,
            "Cookie": f"session={self._session}",
        }
        if extra_headers:
            headers.update(extra_headers)
        return headers

    async def _ensure_authenticated(self) -> None:
        """
        Проверяет авторизацию и переавторизуется при необходимости.

        Raises:
            SessionExpiredError: Если сессия истекла и не удалось переавторизоваться
        """
        if not self._is_authenticated:
            if self._login and self._password:
                logger.info("Session not authenticated, attempting to login")
                await self.login()
            else:
                raise SessionExpiredError(
                    "Session not authenticated and credentials not provided"
                )

    @async_retry(max_attempts=2, delay=1.0, exceptions=(NetworkError,))
    async def fetch_order_details(
        self, order_index: int, is_completed: bool = False
    ) -> Optional[str]:
        """
        Получает описание заказа.

        Args:
            order_index: Индекс заказа
            is_completed: Флаг выполненного заказа

        Returns:
            Описание заказа или None
        """
        await self._ensure_authenticated()

        try:
            url = (
                Get.FETCH_COMPLETED_ORDER_DETAILS if is_completed else Get.FETCH_ORDERS
            )
            response = await self.http_client.request_raw(
                url=url,
                method="POST",
                data={"action": "view", "index": str(order_index)},
                headers=self._get_auth_headers({"Referer": url}),
            )

            if response.status != 200:
                logger.error(f"Failed to fetch order details for {order_index}: {response.status}")
                return None

            html = await response.text()
            return self.parser.parse_description_from_html(html)

        except Exception as e:
            logger.error(f"Error fetching order details: {e}")
            raise NetworkError(f"Failed to fetch order details: {e}") from e

    @async_retry(max_attempts=2, delay=1.0, exceptions=(NetworkError,))
    async def get_order_files(self, order_index: int) -> List[File]:
        """
        Получает список файлов заказа.

        Args:
            order_index: Индекс заказа

        Returns:
            Список файлов
        """
        await self._ensure_authenticated()

        try:
            url = Get.ORDER_FILES.format(order_index=order_index)
            response = await self.http_client.request_raw(
                url=url,
                method="GET",
                headers=self._get_auth_headers({
                    "Referer": url,
                    "X-Requested-With": "XMLHttpRequest",
                }),
            )

            if response.status != 200:
                logger.error(f"Failed to fetch files for order {order_index}: {response.status}")
                return []

            html = await response.text()
            files = self.parser.parse_files_from_html(html)
            logger.info(f"Found {len(files)} files for order {order_index}")
            return files

        except Exception as e:
            logger.warning(f"Error fetching order files: {e}")
            return []

    @async_retry(max_attempts=3, delay=1.0, exceptions=(NetworkError,))
    async def download_file(self, file_id: int) -> bytes:
        """
        Скачивает файл по ID.

        Args:
            file_id: ID файла

        Returns:
            Байты файла
        """
        await self._ensure_authenticated()

        try:
            url = Get.DOWNLOAD_FILE.format(file_id=file_id)
            response = await self.http_client.request_raw(
                url=url,
                method="GET",
                headers=self._get_auth_headers({"Referer": url}),
            )

            if response.status != 200:
                logger.error(f"Failed to download file {file_id}: {response.status}")
                return bytes()

            file_bytes = response._body or bytes()
            logger.info(f"File {file_id} downloaded successfully ({len(file_bytes)} bytes)")
            return file_bytes

        except Exception as e:
            logger.error(f"Error downloading file: {e}")
            raise NetworkError(f"Failed to download file: {e}") from e

    @async_retry(max_attempts=2, delay=1.0, exceptions=(NetworkError,))
    async def take_order(self, order_index: int) -> bool:
        """
        Берёт заказ в работу.

        Args:
            order_index: Индекс заказа

        Returns:
            True если заказ взят успешно
        """
        await self._ensure_authenticated()

        try:
            response = await self.http_client.request_raw(
                url=Post.TAKE_ORDER,
                method="POST",
                data={"action": "take", "index": str(order_index)},
                headers=self._get_auth_headers({"Referer": Post.TAKE_ORDER}),
            )

            if response.status != 200:
                logger.error(f"Failed to take order {order_index}: {response.status}")
                return False

            response_text = await response.text()

            if "Now you can write this order" in response_text:
                logger.info(f"Successfully took order {order_index}")
                return True
            else:
                logger.warning(f"Failed to take order {order_index}")
                return False

        except Exception as e:
            logger.error(f"Error taking order: {e}")
            return False

    async def _fetch_orders_with_details(
        self,
        orders: List[Order],
        is_completed: bool = False,
    ) -> List[Order]:
        """
        Параллельно загружает детали и файлы для списка заказов с rate limiting.

        Args:
            orders: Список заказов
            is_completed: Флаг выполненных заказов

        Returns:
            Обновлённый список заказов с деталями
        """
        # Создаём задачи с rate limiting
        detail_tasks = [
            self.rate_limiter.execute(
                self.fetch_order_details(order.order_index, is_completed=is_completed)
            )
            for order in orders
        ]

        file_tasks = [
            self.rate_limiter.execute(self.get_order_files(order.order_index))
            for order in orders
        ]

        # Выполняем параллельно
        descriptions = await asyncio.gather(*detail_tasks, return_exceptions=True)
        files_list = await asyncio.gather(*file_tasks, return_exceptions=True)

        # Заполняем данные
        for order, desc, files in zip(orders, descriptions, files_list):
            order.description = desc if not isinstance(desc, Exception) else None
            order.files = files if not isinstance(files, Exception) else None

        return orders

    async def get_orders(
        self,
        page: int = 1,
        page_size: int = 50,
        category: str = "essay",
    ) -> List[Order]:
        """
        Получает список доступных заказов.

        Args:
            page: Номер страницы
            page_size: Количество заказов на странице
            category: Категория заказов

        Returns:
            Список заказов
        """
        await self._ensure_authenticated()

        try:
            # Формируем URL с параметрами
            url = f"{Get.ORDERS.split('?')[0]}?mode=free_orders&category={category}&page={page}&pagesize={page_size}&showpages=0"

            response = await self.http_client.request_raw(
                url=url,
                method="GET",
                headers=self._get_auth_headers({"Referer": url}),
            )

            if response.status != 200:
                logger.error(f"Failed to fetch orders: {response.status}")
                return []

            html_content = await response.text()
            orders = self.parser.parse_orders_from_html(html_content, is_completed=False)

            if not orders:
                logger.info("No orders found")
                return []

            # Загружаем детали параллельно с rate limiting
            orders = await self._fetch_orders_with_details(orders, is_completed=False)

            logger.info(f"Fetched {len(orders)} orders successfully")
            return orders

        except Exception as e:
            logger.error(f"Error fetching orders: {e}", exc_info=True)
            raise NetworkError(f"Failed to fetch orders: {e}") from e

    async def get_completed_orders(
        self,
        page: int = 1,
    ) -> List[Order]:
        """
        Получает список выполненных заказов.

        Args:
            page: Номер страницы (если поддерживается)

        Returns:
            Список выполненных заказов
        """
        await self._ensure_authenticated()

        try:
            response = await self.http_client.request_raw(
                url=Get.COMPLETED_ORDERS,
                method="GET",
                headers=self._get_auth_headers({"Referer": Get.COMPLETED_ORDERS}),
            )

            if response.status != 200:
                logger.error(f"Failed to fetch completed orders: {response.status}")
                return []

            html_content = await response.text()
            orders = self.parser.parse_orders_from_html(html_content, is_completed=True)

            if not orders:
                logger.info("No completed orders found")
                return []

            # Загружаем детали параллельно с rate limiting
            orders = await self._fetch_orders_with_details(orders, is_completed=True)

            logger.info(f"Fetched {len(orders)} completed orders successfully")
            return orders

        except Exception as e:
            logger.error(f"Error fetching completed orders: {e}", exc_info=True)
            raise NetworkError(f"Failed to fetch completed orders: {e}") from e
