import asyncio
import datetime
from collections import Counter
from typing import cast

import aiohttp
from astrbot.api import logger

from ..services import EpisodeItem, RenderData, SubjectType
from .base_renderer import BaseRenderer
from .pillow.subject_card import draw_subject_card


def _process_images(data: RenderData) -> None:
    if "image_url" in data:
        return
    images = data.get("images")
    if not isinstance(images, dict):
        return
    images = cast(dict[str, object], images)
    data["image_url"] = (
        images.get("large") or images.get("common") or images.get("medium") or ""
    )


def _process_dates(data: RenderData) -> None:
    if "date" in data:
        return
    if "air_date" in data:
        data["date"] = data["air_date"]


def _process_platform(data: RenderData) -> None:
    if "platform" in data:
        return
    if "type" not in data:
        return
    try:
        type_id = int(data["type"])
        data["platform"] = SubjectType(type_id).to_display()
    except (ValueError, TypeError):
        data["platform"] = "未知"


def _infer_air_weekday(aired_weekdays: list[int]) -> str:
    if not aired_weekdays:
        return ""
    weekday_names = {1: "月", 2: "火", 3: "水", 4: "木", 5: "金", 6: "土", 7: "日"}
    recent = aired_weekdays[-4:]
    most_common = Counter(recent).most_common(1)[0][0]
    return weekday_names.get(most_common, "")


def _parse_episode_list(
    episodes: list[EpisodeItem], today: datetime.date
) -> tuple[list[dict[str, int | bool | None]], list[int]]:
    episode_list: list[dict[str, int | bool | None]] = []
    aired_weekdays: list[int] = []
    for ep in episodes:
        if ep.get("type", 0) != 0 or ep.get("ep", 0) == 0:
            continue
        aired = False
        airdate_str = ep.get("airdate")
        if airdate_str:
            try:
                airdate = datetime.datetime.strptime(airdate_str, "%Y-%m-%d").date()
                aired = airdate <= today
                if aired:
                    aired_weekdays.append(airdate.isoweekday())
            except ValueError:
                pass
        if ep.get("comment", 0) > 0:
            aired = True
        episode_list.append({"ep": ep.get("ep"), "aired": aired})
    return episode_list, aired_weekdays


def _process_episodes(data: RenderData) -> None:
    episodes = data.get("episodes")
    if not isinstance(episodes, list):
        return
    today = datetime.date.today()
    normalized_episodes: list[EpisodeItem] = []
    for episode in episodes:
        if isinstance(episode, dict):
            normalized_episodes.append(cast(EpisodeItem, episode))
    episode_list, aired_weekdays = _parse_episode_list(normalized_episodes, today)
    data["episode_list"] = episode_list
    air_weekday = _infer_air_weekday(aired_weekdays)
    if air_weekday:
        data["air_weekday"] = air_weekday


def preprocess_data(data: RenderData) -> RenderData:
    processed = data.copy()
    _process_images(processed)
    _process_dates(processed)
    _process_platform(processed)
    _process_episodes(processed)
    return processed


class SubjectRenderer(BaseRenderer):
    async def render_subject_card(
        self,
        data: RenderData,
        rpc_url: str | None = None,
        headless: bool = True,
        wait_time: int = 0,
        max_retries: int = 3,
        timeout: int = 30000,
    ) -> str | None:
        render_data = preprocess_data(data)
        return await self.render(
            template_path="subject/subject.html",
            render_data=render_data,
            selector="#card",
            local_render_func=lambda: draw_subject_card(render_data, self._session),
            rpc_url=rpc_url,
            sub_dir="subject",
            timeout=timeout,
            wait_time=wait_time,
        )

    async def render_batch_subject_cards_to_base64(
        self,
        data_list: list[RenderData],
        rpc_url: str | None = None,
        headless: bool = True,
        wait_time: int = 0,
        max_retries: int = 3,
        timeout: int = 30000,
        max_concurrency: int = 3,
    ) -> list[str]:
        semaphore = asyncio.Semaphore(max_concurrency)

        async def _limited_render(data: RenderData) -> str | None:
            async with semaphore:
                return await self.render_subject_card(
                    data=data,
                    rpc_url=rpc_url,
                    headless=headless,
                    wait_time=wait_time,
                    max_retries=max_retries,
                    timeout=timeout,
                )

        tasks = [_limited_render(data) for data in data_list]
        results = await asyncio.gather(*tasks, return_exceptions=True)

        valid_results: list[str] = []
        for i, res in enumerate(results):
            if isinstance(res, Exception):
                logger.warning(f"批量渲染第 {i + 1} 项失败: {res}")
            elif res:
                valid_results.append(res)
        return valid_results
