from __future__ import annotations

from typing import Any

from app.models.financial_record import ExpenseClaim
from app.services.expense_claim_platform_context_tools import (
    collect_context_cities,
    collect_context_item_ids,
    extract_first_known_city_from_text,
    resolve_first_document_field_value,
    unique_text_values,
)
from app.services.expense_rule_runtime import RuntimeTravelPolicy


def resolve_multi_city_related_item_ids(
    claim: ExpenseClaim,
    contexts: list[dict[str, Any]],
    policy: RuntimeTravelPolicy,
) -> tuple[list[str], list[str]]:
    segments = _collect_travel_route_segments(contexts, policy)
    if not segments:
        return collect_context_item_ids(contexts), []

    first_origin = str(segments[0].get("origin") or "").strip()
    first_destination = str(segments[0].get("destination") or "").strip()
    expected_destination = _resolve_expected_travel_city(claim, contexts, policy)
    baseline_cities = unique_text_values(
        [first_origin, expected_destination or first_destination]
    )

    destination_cities = unique_text_values(
        [str(segment.get("destination") or "") for segment in segments]
    )
    extra_cities = [
        city
        for city in destination_cities
        if city and city not in set(baseline_cities)
    ]
    if not extra_cities:
        route_cities = unique_text_values(
            [
                city
                for segment in segments
                for city in (
                    str(segment.get("origin") or ""),
                    str(segment.get("destination") or ""),
                )
            ]
        )
        extra_cities = [
            city
            for city in route_cities
            if city and city not in set(baseline_cities)
        ]

    if not extra_cities:
        return [], []

    affected_segments = [
        segment
        for segment in segments
        if str(segment.get("origin") or "") in extra_cities
        or str(segment.get("destination") or "") in extra_cities
    ]
    return _route_segment_item_ids(affected_segments), extra_cities


def _collect_travel_route_segments(
    contexts: list[dict[str, Any]],
    policy: RuntimeTravelPolicy,
) -> list[dict[str, Any]]:
    segments: list[dict[str, Any]] = []
    for context in list(contexts or []):
        if not isinstance(context, dict) or not _is_long_distance_context(context, policy):
            continue
        route_segment = _extract_route_segment(context, policy)
        if route_segment is None:
            continue
        origin, destination = route_segment
        segments.append(
            {
                "item": context.get("item"),
                "origin": origin,
                "destination": destination,
            }
        )
    return segments


def _resolve_expected_travel_city(
    claim: ExpenseClaim,
    contexts: list[dict[str, Any]],
    policy: RuntimeTravelPolicy,
) -> str:
    claim_city = extract_first_known_city_from_text(str(claim.location or ""), policy)
    if claim_city:
        return claim_city

    for context in list(contexts or []):
        document_info = context.get("document_info") if isinstance(context, dict) else {}
        document_type = str(document_info.get("document_type") or "").strip().lower()
        scene_code = str(document_info.get("scene_code") or "").strip().lower()
        if document_type != "hotel_invoice" and scene_code != "hotel":
            continue
        for city in collect_context_cities(context, policy, include_item_reason=True):
            return city
    return ""


def _extract_route_segment(
    context: dict[str, Any],
    policy: RuntimeTravelPolicy,
) -> tuple[str, str] | None:
    document_info = context.get("document_info") or {}
    item = context.get("item")
    route_value = resolve_first_document_field_value(
        document_info,
        keys={"route", "route_cities", "routecities", "travel_route", "trip_route"},
        labels={"路线", "行程", "起讫", "起终", "始发", "到达"},
    )
    candidates = [
        route_value,
        str(getattr(item, "item_location", "") or ""),
        str(getattr(item, "item_reason", "") or ""),
        str(context.get("ocr_summary") or ""),
        str(context.get("ocr_text") or ""),
    ]
    for candidate in candidates:
        normalized = str(candidate or "").strip()
        if not normalized:
            continue
        for separator in ("-", "—", "–", "至"):
            if separator not in normalized:
                continue
            origin_text, destination_text = [
                segment.strip()
                for segment in normalized.split(separator, 1)
            ]
            origin = extract_first_known_city_from_text(origin_text, policy)
            destination = extract_first_known_city_from_text(destination_text, policy)
            if origin and destination and origin != destination:
                return origin, destination
    return None


def _is_long_distance_context(
    context: dict[str, Any],
    policy: RuntimeTravelPolicy,
) -> bool:
    document_info = context.get("document_info") or {}
    item = context.get("item")
    document_type = str(document_info.get("document_type") or "").strip().lower()
    scene_code = str(document_info.get("scene_code") or "").strip().lower()
    item_type = str(getattr(item, "item_type", "") or "").strip().lower()
    long_distance_types = set(policy.long_distance_document_types)
    return (
        document_type in long_distance_types
        or item_type in long_distance_types
        or scene_code == "travel"
    )


def _route_segment_item_ids(segments: list[dict[str, Any]]) -> list[str]:
    return collect_context_item_ids(
        [
            {"item": segment.get("item")}
            for segment in list(segments or [])
            if isinstance(segment, dict)
        ]
    )
