from __future__ import annotations

import re
from decimal import Decimal
from typing import Any

from app.services.travel_policy_grades import TRAVEL_GRADE_KEYS


def extract_normalized_travel_allowance_limits(
    rows: list[tuple[Any, ...]],
) -> dict[str, dict[str, Decimal]]:
    header_aliases = {
        "region": ("补助区域", "适用区域", "地区", "区域"),
        "meal": ("伙食补助/天", "伙食补助", "餐补", "餐费补助"),
        "basic": ("基本补助/天", "基本出差补贴", "基本补助"),
        "total": ("补助合计/天", "补助合计", "合计", "总计"),
    }
    indexes = _find_header_indexes(rows, header_aliases)
    header_index = indexes.pop("__header_index__", -1)
    if header_index < 0 or "region" not in indexes:
        return {}

    allowance_limits: dict[str, dict[str, Decimal]] = {}
    for row in rows[header_index + 1 :]:
        region = _row_text(row, indexes["region"])
        if not region:
            continue
        for allowance_key in ("meal", "basic", "total"):
            column_index = indexes.get(allowance_key)
            if column_index is None:
                continue
            amount = _coerce_decimal_cell(row[column_index] if len(row) > column_index else None)
            if amount is not None:
                allowance_limits.setdefault(allowance_key, {})[region] = amount
    return allowance_limits


def extract_normalized_transport_class_limits(
    rows: list[tuple[Any, ...]],
) -> dict[str, dict[str, int]]:
    header_aliases = {
        "employee": ("职级范围", "员工职级", "职级"),
        "flight": ("飞机标准", "飞机", "航班标准"),
        "train": ("火车标准", "火车", "铁路标准"),
    }
    indexes = _find_header_indexes(rows, header_aliases)
    header_index = indexes.pop("__header_index__", -1)
    if header_index < 0 or "employee" not in indexes:
        return {}

    limits: dict[str, dict[str, int]] = {}
    for row in rows[header_index + 1 :]:
        employee_text = _row_text(row, indexes["employee"])
        bands = map_transport_grade_row_to_bands(employee_text)
        if not bands:
            continue
        flight_level = (
            transport_class_level_for_text(
                row[indexes["flight"]] if len(row) > indexes["flight"] else None,
                kind="flight",
            )
            if "flight" in indexes
            else None
        )
        train_level = (
            transport_class_level_for_text(
                row[indexes["train"]] if len(row) > indexes["train"] else None,
                kind="train",
            )
            if "train" in indexes
            else None
        )
        for band in bands:
            entry = limits.setdefault(band, {})
            if flight_level is not None:
                entry["flight"] = flight_level
            if train_level is not None:
                entry["train"] = train_level
    return limits


def extract_hotel_season_limits(rows: list[tuple[Any, ...]]) -> dict[str, dict[str, Any]]:
    header_aliases = {
        "city": ("地区（城市）", "城市", "地区"),
        "peak_period": ("旺季期间（月）", "旺季期间", "旺季月份"),
        "peak_limit": ("旺季超标限额", "旺季住宿上限", "旺季限额"),
    }
    indexes = _find_header_indexes_for_aliases(rows, header_aliases, required=("city", "peak_period", "peak_limit"))
    header_index = indexes.pop("__header_index__", -1)
    if header_index < 0:
        return {"hotel_peak_periods": {}, "hotel_peak_city_limits": {}}

    peak_periods: dict[str, str] = {}
    peak_limits: dict[str, Decimal] = {}
    for row in rows[header_index + 1 :]:
        period = _normalize_peak_period_text(_row_text(row, indexes["peak_period"]))
        peak_limit = _coerce_decimal_cell(row[indexes["peak_limit"]] if len(row) > indexes["peak_limit"] else None)
        if not period or peak_limit is None:
            continue
        for city in _split_city_cell(_row_text(row, indexes["city"])):
            peak_periods[city] = period
            peak_limits[city] = peak_limit
    return {"hotel_peak_periods": peak_periods, "hotel_peak_city_limits": peak_limits}


def map_transport_grade_row_to_bands(value: str) -> list[str]:
    normalized = re.sub(r"\s+", "", str(value or "").upper())
    if not normalized or normalized.startswith("注"):
        return []
    if "-" not in normalized and "+" not in normalized and "及以上" not in normalized:
        exact_match = re.search(r"P\s*([0-8])", normalized)
        if exact_match:
            grade_key = f"P{int(exact_match.group(1))}"
            return [grade_key] if grade_key in TRAVEL_GRADE_KEYS else []
    bands: list[str] = []
    if any(
        keyword in normalized
        for keyword in ("P1", "P2", "P3", "P4", "其他员工", "基层经理", "P4及以下")
    ):
        bands.extend(["junior", "mid"])
    if any(
        keyword in normalized
        for keyword in (
            "P5",
            "P6",
            "P7",
            "P5及以上",
            "中层经理",
            "高层经理",
            "公司级",
            "M3",
            "外聘专家",
        )
    ):
        bands.extend(["mid", "senior", "manager", "executive"])
    return list(dict.fromkeys(bands))


def transport_class_level_for_text(value: Any, *, kind: str) -> int | None:
    normalized = re.sub(r"\s+", "", str(value or ""))
    if not normalized:
        return None
    if kind == "flight":
        if any(keyword in normalized for keyword in ("头等舱",)):
            return 4
        if any(keyword in normalized for keyword in ("公务舱", "商务舱")):
            return 3
        if any(keyword in normalized for keyword in ("超级经济舱", "高端经济舱", "明珠经济舱")):
            return 2
        if "经济舱" in normalized:
            return 1
    if kind == "train":
        if "商务座" in normalized:
            return 3
        if any(keyword in normalized for keyword in ("一等座", "软卧")):
            return 2
        if any(keyword in normalized for keyword in ("二等座", "二等软座", "硬卧", "硬座", "硬席")):
            return 1
    return None


def _find_header_indexes(
    rows: list[tuple[Any, ...]],
    header_aliases: dict[str, tuple[str, ...]],
) -> dict[str, int]:
    for index, row in enumerate(rows[:10]):
        indexes: dict[str, int] = {}
        values = [str(value or "").strip() for value in row]
        for key, aliases in header_aliases.items():
            for alias in aliases:
                if alias in values:
                    indexes[key] = values.index(alias)
                    break
        if _has_required_normalized_header(indexes):
            indexes["__header_index__"] = index
            return indexes
    return {}


def _find_header_indexes_for_aliases(
    rows: list[tuple[Any, ...]],
    header_aliases: dict[str, tuple[str, ...]],
    *,
    required: tuple[str, ...],
) -> dict[str, int]:
    for index, row in enumerate(rows[:10]):
        indexes: dict[str, int] = {}
        values = [str(value or "").strip() for value in row]
        for key, aliases in header_aliases.items():
            for alias in aliases:
                if alias in values:
                    indexes[key] = values.index(alias)
                    break
        if all(key in indexes for key in required):
            indexes["__header_index__"] = index
            return indexes
    return {}


def _has_required_normalized_header(indexes: dict[str, int]) -> bool:
    return (
        "region" in indexes
        and any(key in indexes for key in ("meal", "basic", "total"))
    ) or ("employee" in indexes and any(key in indexes for key in ("flight", "train")))


def _row_text(row: tuple[Any, ...], index: int) -> str:
    if index < 0 or len(row) <= index:
        return ""
    return str(row[index] or "").strip()


def _coerce_decimal_cell(value: Any) -> Decimal | None:
    if value is None:
        return None
    try:
        return Decimal(str(value).strip()).quantize(Decimal("0.01"))
    except (ArithmeticError, ValueError):
        return None


def _split_city_cell(value: str) -> list[str]:
    normalized = re.sub(r"[；;，,、/]+", "、", str(value or "").strip())
    if not normalized:
        return []
    names: list[str] = []
    for part in normalized.split("、"):
        cleaned = re.sub(r"\s+", "", part)
        cleaned = re.sub(r"[（(].*?[）)]", "", cleaned)
        if cleaned and len(cleaned) <= 12:
            names.append(cleaned.removesuffix("市"))
    return list(dict.fromkeys(names))


def _normalize_peak_period_text(value: str) -> str:
    text = re.sub(r"\s+", "", str(value or ""))
    text = re.sub(r"(月|上旬|中旬|下旬)", "", text)
    text = re.sub(r"[、，；;]+", ",", text)
    text = re.sub(r"[^0-9,\-]", "", text)
    return re.sub(r",{2,}", ",", text).strip(",")
