import asyncio
import time
import aiohttp
from aiohttp import ClientError
from tenacity import (
    retry,
    stop_after_attempt,
    wait_exponential,
    retry_if_exception_type,
)
from sds_mng.util import logger, raise_for_status

REQUEST_TIMEOUT = 30
DEFAULT_SLEEP = 5
MAX_SIZE = 200
DEFAULT_CONCURRENCY = 5


async def fetch_id(token):
    url = "https://pod-api.sdspod.com/pod/issuing_bay_areas"
    params = {"t": str(int(time.time() * 1000))}
    headers = {
        "access-token": token or "",
    }

    async with aiohttp.ClientSession() as session:
        async with session.get(url, params=params, headers=headers) as resp:
            data = await resp.json()
            raise_for_status(resp, data)
            logger.debug(data)
            return {
                item.get("name", ""): item.get("id", "")
                for item in data.get("items", [])
            }


@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=8),
    retry=retry_if_exception_type(ClientError),
)
async def _fetch(
    token: str, filters=None, sleep=DEFAULT_SLEEP, sem: asyncio.Semaphore = None
):
    async with sem:
        url = "https://pod-api.sdspod.com/pod/logistics/page"
        headers = {"access-token": token}
        filters = (filters or {}).copy()
        keyword = filters.pop("keyword", "")
        delivery_method = filters.pop("deliveryMethod", "FBM")
        issuing_bay_area_id = filters.pop("issuingBayAreaId", "")
        page = filters.pop("page", 1)
        size = filters.pop("size", MAX_SIZE)
        params = {
            "keyword": keyword,
            "deliveryMethod": delivery_method,
            "issuingBayAreaId": issuing_bay_area_id,
            "page": page,
            "size": size,
            "t": int(time.time() * 1000),
            **filters,
        }
        async with aiohttp.ClientSession() as session:
            async with session.get(url, params=params, headers=headers) as resp:
                data = await resp.json()
                raise_for_status(resp, data)
                await asyncio.sleep(sleep)
                return data


async def fetch_logistics(
    token: str,
    filters=None,
    limit=None,
    concurrency=DEFAULT_CONCURRENCY,
    sleep=DEFAULT_SLEEP,
):
    sem = asyncio.Semaphore(concurrency)

    first = await _fetch(
        token, {**(filters or {}), "page": 1, "size": MAX_SIZE}, sleep, sem
    )
    total, items = first.get("totalCount", 0), first.get("items", [])
    target = limit or total
    logger.info(f"已获取 {len(items)}/{target} 条数据")

    if limit and limit <= len(items):
        return {
            "totalCount": total,
            "fetchedCount": limit,
            "list": items[:limit],
        }

    needed = limit or total
    needed_pages = (needed + MAX_SIZE - 1) // MAX_SIZE
    if needed_pages <= 1:
        return {
            "totalCount": total,
            "fetchedCount": len(items),
            "list": items,
        }

    pages = list(range(2, needed_pages + 1))
    tasks = [
        _fetch(token, {**(filters or {}), "page": p, "size": MAX_SIZE}, sleep, sem)
        for p in pages
    ]

    for coro in asyncio.as_completed(tasks):
        data = await coro
        items.extend(data.get("items", []))

    fetched = limit or len(items)
    logger.info(f"已获取 {fetched}/{target} 条数据")
    return {
        "totalCount": total,
        "fetchedCount": fetched,
        "list": items[:limit] if limit else items,
    }
