from __future__ import annotations

import re
from datetime import UTC, date, datetime, timedelta
from typing import Any


def expand_application_time_with_days(
    time_text: str,
    days_text: str,
    *,
    context_json: dict[str, Any] | None = None,
) -> str:
    normalized_time = str(time_text or "").strip()
    days = resolve_application_days_count(days_text)
    if not days:
        return normalized_time

    if normalized_time and re.search(r"\s*(?:至|到|~|-{2,}|—)\s*", normalized_time):
        return normalized_time

    parsed_start = _resolve_start_date(normalized_time, context_json or {})
    if parsed_start is None:
        return normalized_time

    end_date = parsed_start + timedelta(days=max(days - 1, 0))
    start_text = f"{parsed_start:%Y-%m-%d}"
    end_text = f"{end_date:%Y-%m-%d}"
    return start_text if start_text == end_text else f"{start_text} 至 {end_text}"


def resolve_application_days_count(days_text: str) -> int:
    text = str(days_text or "").strip()
    if not text:
        return 0
    digit_match = re.search(r"\d+", text)
    if digit_match:
        return max(0, int(digit_match.group(0)))

    chinese_match = re.search(r"[一二两三四五六七八九十]{1,3}", text)
    if not chinese_match:
        return 0
    return _parse_chinese_number(chinese_match.group(0))


def resolve_application_days_from_time_range(time_text: str) -> int:
    matches = re.findall(
        r"20\d{2}(?:[-/.年])\d{1,2}(?:[-/.月])\d{1,2}日?",
        str(time_text or ""),
    )
    if len(matches) < 2:
        return 0
    start_date = _parse_application_date(matches[0])
    end_date = _parse_application_date(matches[-1])
    if start_date is None or end_date is None or end_date < start_date:
        return 0
    return (end_date - start_date).days + 1


def resolve_application_date_range(time_text: str) -> tuple[date, date] | None:
    matches = re.findall(
        r"20\d{2}(?:[-/.年])\d{1,2}(?:[-/.月])\d{1,2}日?",
        str(time_text or ""),
    )
    dates = [
        parsed
        for parsed in (_parse_application_date(value) for value in matches)
        if parsed is not None
    ]
    if not dates:
        return None
    start_date = dates[0]
    end_date = dates[-1] if len(dates) > 1 else start_date
    if end_date < start_date:
        start_date, end_date = end_date, start_date
    return start_date, end_date


def _resolve_start_date(time_text: str, context_json: dict[str, Any]) -> date | None:
    if time_text:
        match = re.search(
            r"(?P<date>20\d{2}(?:[-/.年])\d{1,2}(?:[-/.月])\d{1,2}日?)",
            time_text,
        )
        if match:
            return _parse_application_date(match.group("date"))
        return None
    return _resolve_client_today(context_json)


def _resolve_client_today(context_json: dict[str, Any]) -> date:
    raw_now = str(context_json.get("client_now_iso") or "").strip()
    parsed_now = _parse_client_now(raw_now)
    if parsed_now is None:
        return datetime.now(UTC).date()

    offset_minutes = _parse_timezone_offset_minutes(
        context_json.get("client_timezone_offset_minutes"),
    )
    if offset_minutes is not None:
        parsed_now = parsed_now - timedelta(minutes=offset_minutes)
    return parsed_now.date()


def _parse_client_now(value: str) -> datetime | None:
    if not value:
        return None
    normalized = value.replace("Z", "+00:00")
    try:
        parsed = datetime.fromisoformat(normalized)
    except ValueError:
        return None
    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=UTC)
    return parsed.astimezone(UTC)


def _parse_timezone_offset_minutes(value: Any) -> int | None:
    try:
        return int(value)
    except (TypeError, ValueError):
        return None


def _parse_chinese_number(value: str) -> int:
    digits = {
        "一": 1,
        "二": 2,
        "两": 2,
        "三": 3,
        "四": 4,
        "五": 5,
        "六": 6,
        "七": 7,
        "八": 8,
        "九": 9,
    }
    text = str(value or "").strip()
    if not text:
        return 0
    if text == "十":
        return 10
    if "十" in text:
        left, _, right = text.partition("十")
        tens = digits.get(left, 1) if left else 1
        ones = digits.get(right, 0) if right else 0
        return tens * 10 + ones
    return digits.get(text, 0)


def _parse_application_date(value: str) -> date | None:
    normalized = str(value or "").strip().rstrip("日").replace("年", "-").replace("月", "-")
    normalized = normalized.replace("/", "-").replace(".", "-")
    parts = [part for part in normalized.split("-") if part]
    if len(parts) != 3:
        return None
    try:
        year, month, day = (int(part) for part in parts)
        return date(year, month, day)
    except ValueError:
        return None
