from __future__ import annotations

from typing import Any


ONTOLOGY_FIELD_ALIASES: dict[str, tuple[str, ...]] = {
    "expense_type": ("reimbursement_type", "scene_label", "expenseType"),
    "time_range": (
        "business_time",
        "businessTime",
        "occurred_date",
        "occurredDate",
        "application_business_time",
        "applicationBusinessTime",
        "application_time",
        "applicationTime",
    ),
    "location": (
        "business_location",
        "businessLocation",
        "application_location",
        "applicationLocation",
    ),
    "reason": (
        "reason_value",
        "reasonValue",
        "business_reason",
        "businessReason",
        "application_reason",
        "applicationReason",
    ),
    "amount": (
        "application_amount",
        "applicationAmount",
        "application_amount_label",
        "applicationAmountLabel",
        "application_budget_occupied_amount",
        "applicationBudgetOccupiedAmount",
        "application_policy_total_amount",
        "applicationPolicyTotalAmount",
    ),
    "transport_estimated_amount": (
        "application_transport_estimated_amount",
        "applicationTransportEstimatedAmount",
        "transportEstimatedAmount",
        "transport_estimate_amount",
        "transportEstimateAmount",
    ),
    "train_estimated_amount": (
        "application_train_estimated_amount",
        "applicationTrainEstimatedAmount",
        "trainEstimatedAmount",
    ),
    "flight_estimated_amount": (
        "application_flight_estimated_amount",
        "applicationFlightEstimatedAmount",
        "flightEstimatedAmount",
    ),
    "hotel_amount": (
        "application_hotel_amount",
        "applicationHotelAmount",
        "lodging_amount",
        "lodgingAmount",
        "hotelAmount",
    ),
    "allowance_amount": (
        "application_allowance_amount",
        "applicationAllowanceAmount",
        "subsidy_amount",
        "subsidyAmount",
        "allowanceAmount",
    ),
    "policy_total_amount": (
        "application_policy_total_amount",
        "applicationPolicyTotalAmount",
        "application_budget_occupied_amount",
        "applicationBudgetOccupiedAmount",
        "policyTotalAmount",
        "budget_occupied_amount",
        "budgetOccupiedAmount",
    ),
    "reimbursement_amount": (
        "application_reimbursement_amount",
        "applicationReimbursementAmount",
        "actual_reimbursement_amount",
        "actualReimbursementAmount",
        "reimbursementAmount",
    ),
    "transport_mode": (
        "transport_type",
        "transportType",
        "transportMode",
        "application_transport_mode",
        "applicationTransportMode",
    ),
    "document_type": ("documentType", "ocr_document_type", "ocrDocumentType"),
    "invoice_no": ("invoiceNo", "invoice_number", "invoiceNumber", "ocr_invoice_no", "ocrInvoiceNo"),
    "invoice_date": ("invoiceDate", "issue_date", "issueDate", "ocr_invoice_date", "ocrInvoiceDate"),
    "ticket_no": ("ticketNo", "ticket_number", "ticketNumber", "ocr_ticket_no", "ocrTicketNo"),
    "ticket_type": ("ticketType", "ocr_ticket_type", "ocrTicketType"),
    "origin_location": ("originLocation", "departure_location", "departureLocation", "from_city", "fromCity"),
    "destination_location": ("destinationLocation", "arrival_location", "arrivalLocation", "to_city", "toCity"),
    "hotel_name": ("hotelName", "ocr_hotel_name", "ocrHotelName"),
    "hotel_nights": ("hotelNights", "stay_nights", "stayNights"),
    "attachments": ("attachment_names", "attachmentNames"),
    "customer_name": ("customerName",),
    "merchant_name": ("merchantName",),
    "cost_center": ("costCenter",),
    "department_name": ("department", "departmentName", "deptName"),
    "employee_grade": ("grade", "user_grade", "employeeGrade", "position_grade"),
    "employee_name": ("name", "user_name", "applicant", "claimant_name", "reporter_name"),
    "employee_no": ("employeeNo",),
    "employee_position": ("position", "employeePosition"),
    "manager_name": ("managerName", "direct_manager_name", "directManagerName"),
    "finance_owner_name": ("financeOwnerName",),
    "finance_approver_name": ("financeApproverName",),
    "basic_rule_code": ("basicRuleCode", "finance_rule_code", "financeRuleCode"),
    "basic_rule_sheet": ("basicRuleSheet", "finance_rule_sheet", "financeRuleSheet"),
    "basic_rule_name": ("basicRuleName", "finance_rule_name", "financeRuleName"),
    "basic_rule_version": ("basicRuleVersion", "finance_rule_version", "financeRuleVersion"),
}

CANONICAL_ONTOLOGY_FIELDS = frozenset(ONTOLOGY_FIELD_ALIASES) | frozenset(
    {
        "participants",
        "department",
        "budget_period",
        "budget_subject",
        "budget_amount",
        "cost_center",
        "warning_threshold",
        "control_action",
        "employee_location",
        "employee_risk_profile",
        "document_id",
        "invoice_no",
        "invoice_date",
        "ticket_no",
        "ticket_type",
        "origin_location",
        "destination_location",
        "hotel_name",
        "hotel_nights",
        "application_claim_id",
        "application_claim_no",
        "application_status",
        "application_amount",
        "application_approved_amount",
        "application_budget_occupied_amount",
        "application_reimbursement_amount",
        "application_expense_type",
        "application_days",
        "application_date",
        "application_required",
        "preapproval_required",
        "application_lodging_daily_cap",
        "application_subsidy_daily_cap",
        "application_transport_policy",
        "application_policy_estimate",
        "application_transport_estimated_amount",
        "application_train_estimated_amount",
        "application_flight_estimated_amount",
        "application_hotel_amount",
        "application_allowance_amount",
        "application_policy_total_amount",
        "application_rule_name",
        "application_rule_version",
        "original_amount",
        "reimbursable_amount",
        "employee_absorbed_amount",
    }
)

ONTOLOGY_CONTEXT_METADATA_FIELDS = frozenset(
    {
        "_claim_no_retry_count",
        "actor",
        "application_edit_claim_id",
        "application_edit_mode",
        "applicationEditClaimId",
        "applicationEditMode",
        "application_fields",
        "application_preview",
        "application_stage",
        "attachment_count",
        "attachment_names",
        "business_time_context",
        "budget_details",
        "budget_header",
        "client_now_iso",
        "client_timezone_offset_minutes",
        "conversation_history",
        "conversation_id",
        "conversation_intent",
        "conversation_scenario",
        "conversation_state",
        "document_type",
        "draft_claim_id",
        "dry_run_email",
        "email",
        "entry_source",
        "expense_scene_selection",
        "force",
        "is_admin",
        "ocr_documents",
        "ocr_summary",
        "report_type",
        "request_context",
        "requested_by_name",
        "requested_by_username",
        "review_action",
        "review_document_form_values",
        "review_form_values",
        "role_codes",
        "role",
        "send_email",
        "session_type",
        "simulate_orchestrator_exception",
        "simulate_tool_failure",
        "time_range_raw",
        "user_id",
        "user_input_text",
        "username",
    }
)

REGISTERED_ONTOLOGY_CONTEXT_FIELDS = (
    CANONICAL_ONTOLOGY_FIELDS
    | ONTOLOGY_CONTEXT_METADATA_FIELDS
    | frozenset(alias for aliases in ONTOLOGY_FIELD_ALIASES.values() for alias in aliases)
)


def normalize_ontology_form_values(values: Any) -> dict[str, str]:
    if not isinstance(values, dict):
        return {}

    normalized: dict[str, str] = {}
    for key, value in values.items():
        cleaned_key = str(key or "").strip()
        if not cleaned_key:
            continue
        normalized[cleaned_key] = str(value or "").strip()

    for canonical_key, aliases in ONTOLOGY_FIELD_ALIASES.items():
        if normalized.get(canonical_key):
            continue
        for alias in aliases:
            if normalized.get(alias):
                normalized[canonical_key] = normalized[alias]
                break

    return normalized


def normalize_ontology_context_json(context_json: Any) -> dict[str, Any]:
    if not isinstance(context_json, dict):
        return {}

    normalized = dict(context_json)
    for canonical_key, aliases in ONTOLOGY_FIELD_ALIASES.items():
        if normalized.get(canonical_key):
            continue
        for alias in aliases:
            if normalized.get(alias):
                normalized[canonical_key] = normalized[alias]
                break
    form_values = normalize_ontology_form_values(normalized.get("review_form_values"))
    if form_values:
        normalized["review_form_values"] = form_values
    return normalized


def is_registered_ontology_context_field(field_name: str) -> bool:
    return str(field_name or "").strip() in REGISTERED_ONTOLOGY_CONTEXT_FIELDS
