from __future__ import annotations

from typing import Any

from app.api.deps import CurrentUserContext
from app.schemas.steward import (
    StewardActionExecuteRequest,
    StewardActionExecuteResponse,
    StewardActionStep,
    StewardTask,
)
from app.services.expense_rule_runtime_defaults import DEFAULT_TRAVEL_POLICY_CONFIG
from app.services.runtime_chat import RuntimeChatService


# 城市分级标签:差旅政策 city_tier 到面向用户的城市档位名称
CITY_TIER_LABELS = {
    "tier_1": "一类城市(北上广深)",
    "tier_2": "二类城市(省会及重点城市)",
    "tier_3": "三类城市(其他地区)",
}

# 交通等级编码到中文标签
TRANSPORT_LEVEL_LABELS = {
    1: "经济舱/二等座(普通席别)",
    2: "高端经济舱/一等座(中级席别)",
    3: "公务舱/商务座(高级席别)",
    4: "头等舱(最高席别)",
}


def build_travel_standard_query_steps(task: StewardTask) -> list[StewardActionStep]:
    """生成差旅标准查询任务的动作步骤。

    查询不产生副作用,无需校验必填、保存或提交,只生成单步执行动作。
    """
    fields = _resolve_task_fields(task)
    return [
        StewardActionStep(
            step_id=f"{task.task_id}:01",
            action_type="execute_travel_standard_query",
            label="查询差旅标准",
            target_task_id=task.task_id,
            status="planned",
            requires_confirmation=False,
            payload={
                "task_id": task.task_id,
                "ontology_fields": fields,
            },
        )
    ]


def execute_travel_standard_query(
    executor: Any,
    request: StewardActionExecuteRequest,
    current_user: CurrentUserContext,
    trace: list[dict[str, Any]],
) -> StewardActionExecuteResponse:
    """执行差旅标准查询:检索业务数据 → 交给 LLM 整理成自然语言。

    数据源为 DEFAULT_TRAVEL_POLICY_CONFIG(住宿标准 by 职级×城市分级、
    交通等级 by 职级)。补助标准当前未纳入运行时配置,用制度说明兜底。
    """
    action_type = "execute_travel_standard_query"
    fields = _resolve_task_fields(request.task)
    message = _resolve_message(request)

    location = str(fields.get("location") or "").strip()
    employee_grade = _resolve_employee_grade(fields, current_user)
    standard_category = str(fields.get("standard_category") or "").strip().lower()

    standards = resolve_travel_standard_snapshot(
        location=location,
        employee_grade=employee_grade,
        standard_category=standard_category,
    )

    if not standards["matched_any"]:
        answer = _build_no_match_answer(location, employee_grade, standard_category)
        return StewardActionExecuteResponse(
            action_type=action_type,
            status="succeeded",
            message=answer,
            result_payload={
                "answer_markdown": answer,
                "standards": standards,
                "matched": False,
            },
            trace=[*trace, _trace("completed", mode="query_no_match")],
        )

    answer = _compose_travel_standard_answer(
        message=message,
        standards=standards,
        location=location,
        employee_grade=employee_grade,
    )
    return StewardActionExecuteResponse(
        action_type=action_type,
        status="succeeded",
        message=answer,
        result_payload={
            "answer_markdown": answer,
            "standards": standards,
            "matched": True,
        },
        trace=[*trace, _trace("completed", mode="query_travel_standard")],
    )


def resolve_travel_standard_snapshot(
    *,
    location: str,
    employee_grade: str,
    standard_category: str = "",
) -> dict[str, Any]:
    """按地点、职级和关注标准类别,从差旅政策配置检索确定性标准数值。

    standard_category 为空表示返回全部类别;非空时只返回指定类别。
    支持的类别:lodging(住宿)、transport(交通)、allowance(补助)。
    """
    config = DEFAULT_TRAVEL_POLICY_CONFIG
    city_tiers = config.get("city_tiers", {})
    hotel_limits = config.get("hotel_limits", {})
    transport_limits = config.get("transport_limits", {})
    band_labels = config.get("band_labels", {})

    normalized_city = str(location or "").strip()
    city_tier = city_tiers.get(normalized_city, "tier_3") if normalized_city else "tier_3"
    normalized_grade = _normalize_grade(employee_grade)

    snapshot: dict[str, Any] = {
        "location": normalized_city or "",
        "city_tier": city_tier,
        "city_tier_label": CITY_TIER_LABELS.get(city_tier, city_tier),
        "employee_grade": normalized_grade,
        "employee_grade_label": band_labels.get(normalized_grade, normalized_grade or "未指定"),
        "standard_category": standard_category or "",
        "matched_any": False,
        "lodging": None,
        "transport": None,
        "allowance": None,
    }

    want_all = not standard_category
    if want_all or standard_category == "lodging":
        lodging_cap = _resolve_lodging_cap(hotel_limits, normalized_grade, city_tier)
        if lodging_cap is not None:
            snapshot["lodging"] = {
                "daily_cap": str(lodging_cap),
                "unit": "元/晚",
            }
            snapshot["matched_any"] = True

    if want_all or standard_category == "transport":
        transport_band = _resolve_transport_band(transport_limits, normalized_grade)
        if transport_band is not None:
            snapshot["transport"] = {
                "flight_level": transport_band.get("flight"),
                "train_level": transport_band.get("train"),
                "flight_label": TRANSPORT_LEVEL_LABELS.get(int(transport_band.get("flight", 0)), ""),
                "train_label": TRANSPORT_LEVEL_LABELS.get(int(transport_band.get("train", 0)), ""),
            }
            snapshot["matched_any"] = True

    if want_all or standard_category == "allowance":
        # 补助标准当前未纳入运行时配置,用占位说明,等补助数据源接入后补全。
        # 占位说明不计入 matched_any,避免无有效数据时仍标记为已匹配。
        snapshot["allowance"] = {
            "note": "出差补助标准按地区(直辖市/港澳台/境外等)分档,具体数值请参考《公司差旅费报销规则》或咨询财务。",
        }

    return snapshot


def _resolve_lodging_cap(
    hotel_limits: dict[str, Any],
    grade: str,
    city_tier: str,
) -> str | None:
    grade_entry = hotel_limits.get(grade)
    if not isinstance(grade_entry, dict):
        return None
    cap = grade_entry.get(city_tier)
    return str(cap).strip() if cap is not None else None


def _resolve_transport_band(
    transport_limits: dict[str, Any],
    grade: str,
) -> dict[str, Any] | None:
    band = transport_limits.get(grade)
    if not isinstance(band, dict):
        return None
    return {"flight": band.get("flight"), "train": band.get("train")}


def _normalize_grade(value: str) -> str:
    normalized = str(value or "").strip().upper()
    if normalized in {"", "未指定", "未知"}:
        return ""
    if normalized in DEFAULT_TRAVEL_POLICY_CONFIG.get("band_labels", {}):
        return normalized
    # 容忍 P05 / p5 等写法
    compact = normalized.lstrip("Pp")
    if compact.isdigit():
        candidate = f"P{int(compact)}"
        if candidate in DEFAULT_TRAVEL_POLICY_CONFIG.get("band_labels", {}):
            return candidate
    return normalized


def _resolve_employee_grade(
    fields: dict[str, str],
    current_user: CurrentUserContext,
) -> str:
    grade = str(fields.get("employee_grade") or "").strip()
    if grade:
        return grade
    return str(getattr(current_user, "grade", "") or "").strip()


def _resolve_task_fields(task: StewardTask | None) -> dict[str, str]:
    if task is None or not isinstance(task.ontology_fields, dict):
        return {}
    return {
        str(key or "").strip(): str(value or "").strip()
        for key, value in task.ontology_fields.items()
        if str(key or "").strip() and str(value or "").strip()
    }


def _resolve_message(request: StewardActionExecuteRequest) -> str:
    message = str(request.message or "").strip()
    if message:
        return message
    if request.task is not None:
        return str(request.task.summary or request.task.title or "").strip()
    return "差旅标准查询"


def _build_no_match_answer(location: str, employee_grade: str, standard_category: str) -> str:
    parts = ["### 未能匹配到具体差旅标准"]
    details = []
    if location:
        details.append(f"目的地:**{location}**")
    if employee_grade:
        details.append(f"职级:**{employee_grade}**")
    if standard_category:
        details.append(f"关注类别:**{standard_category}**")
    if details:
        parts.append("")
        parts.append("当前识别到:" + "、".join(details) + "。")
    parts.append("")
    parts.append(
        "请补充更明确的信息,例如\"P5 去武汉出差的住宿标准是多少\","
        "或直接说\"查武汉的住宿标准\"。"
    )
    return "\n".join(parts)


def _compose_travel_standard_answer(
    *,
    message: str,
    standards: dict[str, Any],
    location: str,
    employee_grade: str,
) -> str:
    """把结构化标准整理成面向用户的 Markdown 回复。

    优先用确定性数据拼装;如需更自然的表述,可在此处接入 LLM,
    当前阶段确定性拼装已足够清晰,避免额外模型调用开销。
    """
    lines = ["### 差旅标准查询结果"]
    context_parts = []
    if location:
        context_parts.append(f"目的地 **{location}**({standards.get('city_tier_label', '')})")
    if employee_grade:
        context_parts.append(f"职级 **{standards.get('employee_grade_label', employee_grade)}**")
    if context_parts:
        lines.append("")
        lines.append("查询条件:" + "、".join(context_parts) + "。")

    lodging = standards.get("lodging")
    transport = standards.get("transport")
    allowance = standards.get("allowance")

    if lodging:
        lines.append("")
        lines.append(f"- **住宿标准**:{lodging['daily_cap']} {lodging['unit']}")
    if transport:
        lines.append(
            f"- **交通工具等级**:飞机 {transport.get('flight_label', '')}、"
            f"火车 {transport.get('train_label', '')}"
        )
    if allowance:
        lines.append(f"- **出差补助**:{allowance.get('note', '')}")

    lines.append("")
    lines.append(
        "> 标准依据公司差旅政策运行时配置。如需了解超标说明、多城市行程等例外口径,"
        "请进一步描述您的场景。"
    )
    return "\n".join(lines)


def _trace(stage: str, **extra: Any) -> dict[str, Any]:
    from datetime import UTC, datetime

    return {
        "stage": stage,
        "at": datetime.now(UTC).isoformat(),
        **extra,
    }
