from __future__ import annotations

import re
from typing import Any

from app.services.expense_rule_runtime import RuntimeTravelPolicy


def unique_text_values(values: list[Any]) -> list[str]:
    normalized_values: list[str] = []
    seen: set[str] = set()
    for value in list(values or []):
        normalized = str(value or "").strip()
        if not normalized or normalized in seen:
            continue
        seen.add(normalized)
        normalized_values.append(normalized)
    return normalized_values


def count_values(values: list[str]) -> dict[str, int]:
    counts: dict[str, int] = {}
    for value in values:
        normalized = str(value or "").strip()
        if not normalized:
            continue
        counts[normalized] = counts.get(normalized, 0) + 1
    return counts


def collect_invoice_keys_from_contexts(contexts: list[dict[str, Any]]) -> list[str]:
    invoice_keys: list[str] = []
    for context in contexts:
        document_info = context.get("document_info") or {}
        for key in collect_invoice_keys_from_document_info(document_info):
            if key not in invoice_keys:
                invoice_keys.append(key)
    return invoice_keys


def collect_invoice_keys_from_document_info(document_info: dict[str, Any]) -> list[str]:
    keys: list[str] = []
    for field in list(document_info.get("fields") or []):
        if not isinstance(field, dict):
            continue
        field_key = str(field.get("key") or "").strip().lower().replace("_", "")
        label = str(field.get("label") or "").replace(" ", "")
        value = str(field.get("value") or "").strip()
        if not value:
            continue
        if field_key in {"invoiceno", "invoicenumber", "number", "code"} or any(
            token in label for token in ("发票号码", "票号", "发票代码", "号码")
        ):
            normalized = re.sub(r"\s+", "", value)
            if normalized and normalized not in keys:
                keys.append(normalized)
    return keys


def collect_attachment_cities(
    contexts: list[dict[str, Any]],
    policy: RuntimeTravelPolicy,
) -> list[str]:
    cities: list[str] = []
    for context in contexts:
        for city in collect_context_cities(context, policy):
            if city not in cities:
                cities.append(city)
    return cities


def collect_context_cities(
    context: dict[str, Any],
    policy: RuntimeTravelPolicy,
    *,
    include_item_reason: bool = False,
) -> list[str]:
    if not isinstance(context, dict):
        return []
    document_info = context.get("document_info") or {}
    item = context.get("item")
    parts = [
        str(context.get("ocr_summary") or ""),
        str(context.get("ocr_text") or ""),
        str(getattr(item, "item_location", "") or ""),
    ]
    if include_item_reason:
        parts.append(str(getattr(item, "item_reason", "") or ""))
    for field in list(document_info.get("fields") or []):
        if isinstance(field, dict):
            parts.append(str(field.get("value") or ""))
    return extract_known_cities_from_text(" ".join(parts), policy)


def extract_known_cities_from_text(text: str, policy: RuntimeTravelPolicy) -> list[str]:
    normalized = str(text or "").strip()
    if not normalized:
        return []
    cities: list[str] = []
    for city in sorted(policy.city_tiers.keys(), key=lambda item: len(item), reverse=True):
        if city in normalized and city not in cities:
            cities.append(city)
    return cities


def extract_first_known_city_from_text(text: str, policy: RuntimeTravelPolicy) -> str:
    cities = extract_known_cities_from_text(text, policy)
    return cities[0] if cities else ""


def resolve_first_document_field_value(
    document_info: dict[str, Any],
    *,
    keys: set[str],
    labels: set[str],
) -> str:
    normalized_keys = {key.replace("_", "").lower() for key in keys}
    for field in list(document_info.get("fields") or []):
        if not isinstance(field, dict):
            continue
        field_key = str(field.get("key") or "").strip().lower().replace("_", "")
        label = str(field.get("label") or "").replace(" ", "")
        value = str(field.get("value") or "").strip()
        if not value:
            continue
        if field_key in normalized_keys or any(token in label for token in labels):
            return value
    return ""


def collect_context_item_ids(contexts: list[dict[str, Any]]) -> list[str]:
    item_ids: list[str] = []
    seen: set[str] = set()
    for context in list(contexts or []):
        item = context.get("item") if isinstance(context, dict) else None
        item_id = str(getattr(item, "id", "") or "").strip()
        if item_id and item_id not in seen:
            seen.add(item_id)
            item_ids.append(item_id)
    return item_ids
