from __future__ import annotations

import unittest

from amundsen.core.distance_calculator import DistanceCalculator
from amundsen.core.models import GPXDocument, Track, TrackPoint, TrackSegment, Waypoint
from amundsen.core.smart_gpx_combiner import SmartCombineSource, SmartGPXCombiner


class SmartGPXCombinerTest(unittest.TestCase):
    def test_greedy_combiner_reorders_from_middle_and_reverses_sources(self) -> None:
        combiner = SmartGPXCombiner(DistanceCalculator())
        input_order = [5, 2, 8, 0, 10, 4, 6, 1, 9, 3, 7]
        reversed_segments = {2, 7, 10}
        sources = [
            self._source(segment_index, reverse=segment_index in reversed_segments)
            for segment_index in input_order
        ]

        plan = combiner.build_plan(sources, "Geometrisch geplakt")
        points = plan["document"].all_points()
        normalized_lons = [round((point.lon - 7.0) / 0.001) for point in points]
        forward = list(range(12))
        backward = list(reversed(forward))

        self.assertIn(normalized_lons, [forward, backward])
        self.assertEqual(plan["summary"]["connection_gap_km"], 0.0)
        self.assertGreaterEqual(sum(row["reversed"] for row in plan["routes"]), 1)
        self.assertFalse(any(layer["kind"] == "missing" for layer in plan["map_layers"]))

    def test_build_plan_from_selection_uses_manual_order_and_direction(self) -> None:
        combiner = SmartGPXCombiner(DistanceCalculator())
        sources = [self._source(0), self._source(1), self._source(2)]

        plan = combiner.build_plan_from_selection(sources, "Handmatig", [(2, True), (0, False), (1, False)])
        points = plan["document"].all_points()
        normalized_lons = [round((point.lon - 7.0) / 0.001) for point in points]

        self.assertEqual(normalized_lons, [3, 2, 0, 1, 2])
        self.assertEqual([row["source_index"] for row in plan["routes"]], [2, 0, 1])
        self.assertTrue(plan["routes"][0]["reversed"])
        self.assertEqual(plan["source_layers"][0]["waypoints"][0]["name"], "Punt 0")

    def _source(self, segment_index: int, reverse: bool = False) -> SmartCombineSource:
        points = [
            TrackPoint(lat=46.0, lon=7.0 + segment_index * 0.001, elevation=1000 + segment_index),
            TrackPoint(lat=46.0, lon=7.0 + (segment_index + 1) * 0.001, elevation=1001 + segment_index),
        ]
        if reverse:
            points.reverse()
        name = f"Segment {segment_index:02d}"
        document = GPXDocument(
            name=name,
            tracks=[Track(name=name, segments=[TrackSegment(points=points)])],
            waypoints=[Waypoint(points[0].lat, points[0].lon, f"Punt {segment_index}", points[0].elevation)],
        )
        return SmartCombineSource(document=document, filename=f"{name}.gpx", raw_bytes=b"", xml_text="")


if __name__ == "__main__":
    unittest.main()
