"""Compact server-side Explorer projection for frozen mapped-character evidence."""

from __future__ import annotations

from typing import Any, Sequence

from mountain_twin.exposure import RoutePoint
from mountain_twin.pace.analysis import analyze_route_pace
from mountain_twin.route_analysis import prepare_route
from mountain_twin.trail_character.association import DEFAULT_ASSOCIATION_POLICY, associate_route
from mountain_twin.trail_character.continuity import derive_association_continuity
from mountain_twin.trail_character.contracts import CharacterCoverage, GeoCoordinate
from mountain_twin.trail_character.osm_fixture import FrozenOsmFixture
from mountain_twin.trail_character.terrain import compose_terrain_character

EXPLORER_TRAIL_CHARACTER_CONTRACT_VERSION = "trail_terrain_character_explorer_v0_1"


def compose_frozen_fixture_character(route_id: str, route_points: Sequence[Any], fixture):
    """Compose the canonical result over a locally frozen route section only."""
    local_points = tuple(
        RoutePoint(
            route_id,
            index,
            getattr(point, "track_index", 0),
            getattr(point, "segment_index", 0),
            point.latitude,
            point.longitude,
            getattr(point, "elevation_m", getattr(point, "gpx_elevation_m", None)),
        )
        for index, point in enumerate(route_points)
    )
    prepared = prepare_route(local_points).points
    association = associate_route(
        tuple(GeoCoordinate(point.latitude, point.longitude) for point in prepared),
        fixture.features,
        candidate_coverage=CharacterCoverage.FULL,
        policy=DEFAULT_ASSOCIATION_POLICY,
    )
    return compose_terrain_character(
        prepared,
        analyze_route_pace(prepared, scenario_name="NOMINAL", scenario_factor=1.0),
        route_input_reference=f"frozen_osm_fixture:{fixture.fixture_id}",
        mapped_association=association,
        mapped_features=fixture.features,
        mapped_continuity=derive_association_continuity(association),
    )


def project_trail_terrain_character(result, fixture: FrozenOsmFixture, *, route_distance_offset_m: float):
    """Return a browser-ready compact projection without route/source geometry matrices."""
    document = result.to_dict()
    mapped = document["mapped_character"]
    source_features = {feature.feature_id: feature for feature in fixture.features}
    feature_ids = sorted(
        {feature_id for segment in document["mapped_character_segments"] for feature_id in segment["source_feature_ids"]}
    )
    return {
        "contract_version": EXPLORER_TRAIL_CHARACTER_CONTRACT_VERSION,
        "availability": "AVAILABLE",
        "route_reference": {
            "route_id": result.route.route_id,
            "section_distance_offset_m": route_distance_offset_m,
            "section_distance_m": result.route.distance_m,
        },
        "terrain_character": document["terrain_character"],
        "mapped_character": {
            "coverage": mapped["coverage"],
            "normalization_policy": mapped["normalization_policy"],
            "association_policy": document["mapped_character"]["association"]["policy"],
            "characteristics": mapped["characteristics"],
            "segments": document["mapped_character_segments"],
        },
        "source_inventory": [
            {
                "source_id": fixture.source.source_id,
                "provider": fixture.source.provider,
                "dataset_snapshot_reference": fixture.source.dataset_snapshot_reference,
                "retrieved_at": fixture.source.retrieved_at,
                "attribution": fixture.source.attribution,
                "license": fixture.source.license,
            }
        ],
        "feature_provenance": [
            {
                "feature_id": feature_id,
                "source_element_version": source_features[feature_id].source_element_version,
                "source_updated_at": source_features[feature_id].source_updated_at,
            }
            for feature_id in feature_ids
        ],
        "limitations": [
            "Mapped character is frozen source evidence, not current trail status, condition, legality, suitability, or traversability.",
            "OSM element timestamps are map-edit metadata, not field verification time.",
            "The mapped-character section is bounded by the frozen fixture coverage.",
        ],
    }
