from __future__ import annotations

from datetime import datetime, timedelta
from typing import Iterable, Optional, Union

from flow_xhs.runtime.errors import InvalidPublishParameterError, UnsupportedPublishTargetError
from flow_xhs.runtime.publishing.models import PublishRequest, PublishTarget


def calc_title_length(title: str) -> int:
    byte_len = 0
    for ch in title:
        byte_len += 2 if ord(ch) > 127 else 1
    return (byte_len + 1) // 2


def parse_rfc3339(value: str) -> datetime:
    text = value.strip()
    if text.endswith("Z"):
        text = text[:-1] + "+00:00"
    dt = datetime.fromisoformat(text)
    if dt.tzinfo is None:
        raise InvalidPublishParameterError("`schedule_at` 必须包含时区，例如 +08:00")
    return dt


def _normalize_target(target: Union[str, PublishTarget]) -> PublishTarget:
    if isinstance(target, PublishTarget):
        return target
    if not target:
        raise InvalidPublishParameterError("`target` 不能为空")
    text = str(target).strip().lower()
    for item in PublishTarget:
        if item.value == text:
            return item
    raise UnsupportedPublishTargetError(f"不支持的 target: {target}，当前支持: image, video, article")


def _normalize_text_list(items: Optional[Iterable], field_name: str) -> list[str]:
    if items is None:
        return []
    if not isinstance(items, (list, tuple)):
        raise InvalidPublishParameterError(f"`{field_name}` 必须是 list 或 tuple")

    normalized: list[str] = []
    for index, item in enumerate(items):
        text = "" if item is None else str(item).strip()
        if not text:
            raise InvalidPublishParameterError(f"`{field_name}` 第 {index + 1} 项不能为空")
        normalized.append(text)
    return normalized


def _normalize_media_list(media_list: Optional[Iterable], required: bool) -> list[str]:
    if media_list is None and not required:
        return []
    normalized = _normalize_text_list(media_list, "media_list")
    if required and not normalized:
        raise InvalidPublishParameterError("`media_list` 至少包含 1 个素材")
    return normalized


def _normalize_schedule(schedule_at: Optional[Union[str, datetime]]) -> Optional[datetime]:
    if schedule_at is None:
        return None
    if isinstance(schedule_at, datetime):
        dt = schedule_at
    else:
        dt = parse_rfc3339(str(schedule_at))

    if dt.tzinfo is None:
        raise InvalidPublishParameterError("`schedule_at` 必须包含时区，例如 +08:00")

    now = datetime.now(dt.tzinfo)
    min_time = now + timedelta(hours=1)
    max_time = now + timedelta(days=14)
    if dt < min_time:
        raise InvalidPublishParameterError(
            f"定时发布时间至少在 1 小时后，当前: {dt.strftime('%Y-%m-%d %H:%M')}, 最早: {min_time.strftime('%Y-%m-%d %H:%M')}"
        )
    if dt > max_time:
        raise InvalidPublishParameterError(
            f"定时发布时间不能超过 14 天，当前: {dt.strftime('%Y-%m-%d %H:%M')}, 最晚: {max_time.strftime('%Y-%m-%d %H:%M')}"
        )
    return dt


def normalize_publish_request(
        *,
        target: Union[str, PublishTarget],
        title: str = "",
        content: str = "",
        media_list: Optional[Iterable] = None,
        tags: Optional[Iterable] = None,
        schedule_at: Optional[Union[str, datetime]] = None,
        account_uid: Optional[str] = None,
) -> PublishRequest:
    normalized_target = _normalize_target(target)
    media_required = normalized_target in {PublishTarget.IMAGE, PublishTarget.VIDEO}
    normalized_media = _normalize_media_list(media_list, required=media_required)
    normalized_tags = _normalize_text_list(tags, "tags")

    if normalized_target == PublishTarget.VIDEO and len(normalized_media) != 1:
        raise InvalidPublishParameterError("`target=video` 时 `media_list` 必须且仅包含 1 个视频素材")

    title_text = (title or "").strip()
    content_text = (content or "").strip()
    if title_text and calc_title_length(title_text) > 20:
        raise InvalidPublishParameterError("标题长度超过限制（最多 20）")

    if len(normalized_tags) > 10:
        normalized_tags = normalized_tags[:10]

    normalized_schedule = _normalize_schedule(schedule_at)

    return PublishRequest(
        target=normalized_target,
        title=title_text,
        content=content_text,
        media_list=normalized_media,
        tags=normalized_tags,
        schedule_at=normalized_schedule,
        account_uid=(account_uid or "").strip() or None,
    )
