import itertools
import re
from collections import defaultdict
from typing import Any, Dict, List, Optional, Tuple

import arrow
from httpx import AsyncClient
from PicImageSearch import EHentai
from PicImageSearch.model import EHentaiResponse
from pyquery import PyQuery

from .ascii2d import ascii2d_search
from .config import config
from .utils import (
    DEFAULT_HEADERS,
    SEARCH_FUNCTION_TYPE,
    async_lock,
    filter_results_with_ratio,
    handle_img,
    parse_cookies,
    preprocess_search_query,
    shorten_url,
)


@async_lock(freq=8)
async def ehentai_search(
    url: str, client: AsyncClient
) -> Tuple[List[str], Optional[SEARCH_FUNCTION_TYPE]]:
    ex = bool(config.exhentai_cookies)
    ehentai = EHentai(client=client)

    if res := await ehentai.search(url, ex=ex):
        if "Please wait a bit longer between each file search" in res.origin:
            return await ehentai_search(url, client)

        final_res: List[str] = await search_result_filter(res)
        if not res.raw and config.auto_use_ascii2d:
            final_res.append("自动使用 Ascii2D 进行搜索")
            return final_res, ascii2d_search

        return final_res, None

    return ["EHentai 暂时无法使用"], None


async def ehentai_title_search(title: str) -> List[str]:
    query = preprocess_search_query(title)
    url = "https://exhentai.org" if config.exhentai_cookies else "https://e-hentai.org"
    params: Dict[str, Any] = {"f_search": query}

    async with AsyncClient(
        headers=DEFAULT_HEADERS,
        cookies=parse_cookies(config.exhentai_cookies),
        proxies=config.proxy,
    ) as session:
        resp = await session.get(url, params=params)
        if res := EHentaiResponse(resp.text, str(resp.url)):
            if not res.raw:
                # 如果第一次没找到，使搜索结果包含被删除的部分，并重新搜索
                params["advsearch"] = 1
                params["f_sname"] = "on"
                params["f_sh"] = "on"
                resp = await session.get(url, params=params)
                res = EHentaiResponse(resp.text, str(resp.url))

            # 只保留标题和搜索关键词相关度较高的结果，并排序，以此来提高准确度
            if res.raw:
                res.raw = filter_results_with_ratio(res, title)
            return await search_result_filter(res)

        return ["EHentai 暂时无法使用"]


async def search_result_filter(
    res: EHentaiResponse,
) -> List[str]:
    url = await shorten_url(res.url)
    if not res.raw:
        return [f"EHentai 搜索结果为空\n搜索页面：{url}"]

    # 尝试过滤已删除的
    if not_expunged_res := [
        i for i in res.raw if not PyQuery(i.origin)("[id^='posted'] s")
    ]:
        res.raw = not_expunged_res

    # 尝试过滤无主题的杂图图集
    if not_themeless_res := [i for i in res.raw if "themeless" not in " ".join(i.tags)]:
        res.raw = not_themeless_res

    # 尝试过滤评分低于 3 星的
    if above_3_star_res := [
        i
        for i in res.raw
        if get_star_rating(PyQuery(i.origin)("div.ir").attr("style")) >= 3
    ]:
        res.raw = above_3_star_res

    # 尽可能过滤掉非预期结果(大概
    priority = defaultdict(lambda: 0)
    priority["Image Set"] = 1
    priority["Non-H"] = 2
    priority["Western"] = 3
    priority["Misc"] = 4
    priority["Cosplay"] = 5
    priority["Asian Porn"] = 6
    res.raw.sort(key=lambda x: priority[x.type], reverse=True)
    for key, group in itertools.groupby(res.raw, key=lambda x: x.type):  # type: ignore
        if priority[key] > 0:
            group_list = list(group)
            if len(res.raw) != len(group_list):
                res.raw = [i for i in res.raw if i not in group_list]

    # 优先找汉化版；没找到就优先找原版
    if chinese_res := [
        i
        for i in res.raw
        if "translated" in " ".join(i.tags) and "chinese" in " ".join(i.tags)
    ]:
        selected_res = chinese_res[0]
    elif not_translated_res := [
        i for i in res.raw if "translated" not in " ".join(i.tags)
    ]:
        selected_res = not_translated_res[0]
    else:
        selected_res = res.raw[0]

    thumbnail = await handle_img(
        selected_res.thumbnail, cookies=config.exhentai_cookies
    )
    date = arrow.get(selected_res.date).to("Asia/Shanghai").format("YYYY-MM-DD HH:mm")
    favorited = bool(selected_res.origin.find("[id^='posted']").eq(0).attr("style"))
    res_list = [
        "EHentai 搜索结果",
        thumbnail,
        selected_res.title,
        "❤️ 已收藏" if favorited else "",
        f"类型：{selected_res.type}",
        f"日期：{date}",
        f"来源：{selected_res.url}",
        f"搜索页面：{url}",
    ]
    return ["\n".join([i for i in res_list if i])]


def get_star_rating(css_style: str) -> float:
    x, y = re.search(r"(-?\d+)px (-\d+)px", css_style).groups()  # type: ignore
    star_rating = 5 - int(x.rstrip("px")) / -16
    if y == "-21px":
        star_rating -= 0.5
    return star_rating
