import types
from collections import namedtuple

import pytest

from target_simulator.gui import trajectory_editor_window as tew


class DummyVar:
    def __init__(self, value=False):
        self._value = value

    def get(self):
        return self._value

    def set(self, v):
        self._value = v


class FakeTree:
    def __init__(self):
        self.items = {}
        self._focus = ""

    def delete(self, *args, **kwargs):
        self.items.clear()

    def get_children(self):
        return list(self.items.keys())

    def insert(self, parent, pos, iid=None, values=None):
        self.items[iid] = values

    def see(self, item_id):
        pass

    def focus(self):
        return self._focus

    def focus_set(self, v):
        self._focus = v

    def selection_set(self, v):
        self._focus = v

    def config(self, **kwargs):
        pass


class DummyLabel:
    def __init__(self):
        self.text = ""

    def config(self, **kwargs):
        self.text = kwargs.get("text", self.text)


def _make_window():
    # Create instance without running __init__ (which blocks on wait_window)
    inst = tew.TrajectoryEditorWindow.__new__(tew.TrajectoryEditorWindow)
    inst.existing_ids = []
    inst.result_target = None
    inst.initial_max_range = 10
    inst.time_multiplier = 1.0
    inst.total_sim_time = 0.0
    inst.waypoints = []
    inst.use_spline_var = DummyVar(False)
    inst.gui_update_queue = None
    inst.preview_engine = None
    inst.is_preview_running = DummyVar(False)
    inst.is_paused = DummyVar(False)
    inst.wp_tree = FakeTree()
    inst.ppi_preview = types.SimpleNamespace(draw_trajectory_preview=lambda **k: None, update_targets=lambda u: None)
    inst.sim_progress_var = DummyVar(0.0)
    inst.sim_time_label = DummyLabel()
    inst.time_multiplier_var = types.SimpleNamespace(get=lambda: "1x")
    inst.multiplier_combo = types.SimpleNamespace(config=lambda **k: None)
    inst.play_button = types.SimpleNamespace(config=lambda **k: None)
    inst.pause_button = types.SimpleNamespace(config=lambda **k: None)
    inst.stop_button = types.SimpleNamespace(config=lambda **k: None)
    return inst


def test_on_spline_toggle_shows_info_when_too_few_waypoints(monkeypatch):
    inst = _make_window()
    inst.waypoints = [1, 2]  # fewer than 4
    inst.use_spline_var.set(True)

    called = {}

    def fake_showinfo(title, message, parent=None):
        called["title"] = title
        called["message"] = message

    monkeypatch.setattr(tew.messagebox, "showinfo", fake_showinfo)
    inst._on_spline_toggle()
    assert called.get("title") == "Spline Not Available"
    assert inst.use_spline_var.get() is False


def test_on_time_multiplier_changed_valid_and_invalid():
    inst = _make_window()

    inst.time_multiplier_var = types.SimpleNamespace(get=lambda: "2x")
    inst.preview_engine = None
    inst._on_time_multiplier_changed()
    assert inst.time_multiplier == 2.0

    inst.time_multiplier_var = types.SimpleNamespace(get=lambda: "bad")
    inst._on_time_multiplier_changed()
    assert inst.time_multiplier == 1.0


def test_on_ok_invalid_first_waypoint_shows_error(monkeypatch):
    inst = _make_window()
    inst.waypoints = []

    captured = {}

    def fake_showerror(title, message, parent=None):
        captured["title"] = title
        captured["message"] = message

    monkeypatch.setattr(tew.messagebox, "showerror", fake_showerror)
    inst._on_ok()
    assert captured.get("title") == "Invalid Trajectory"


def test_on_remove_waypoint_no_selection_and_single_waypoint(monkeypatch):
    inst = _make_window()

    # No selection
    inst.wp_tree._focus = ""
    captured = {}

    def fake_showwarning(title, message, parent=None):
        captured["warn"] = (title, message)

    monkeypatch.setattr(tew.messagebox, "showwarning", fake_showwarning)
    inst._on_remove_waypoint()
    assert captured.get("warn")[0] == "No Selection"

    # Single waypoint present -> error on remove
    inst.wp_tree._focus = "0"
    inst.waypoints = [object()]

    def fake_showerror(title, message, parent=None):
        captured["err"] = (title, message)

    monkeypatch.setattr(tew.messagebox, "showerror", fake_showerror)
    inst._on_remove_waypoint()
    assert captured.get("err")[0] == "Action Denied"


def test_populate_waypoint_list_and_get_total_time(monkeypatch):
    inst = _make_window()

    # Create dummy waypoint objects matching expected attributes
    WP = namedtuple(
        "WP",
        [
            "maneuver_type",
            "target_range_nm",
            "target_azimuth_deg",
            "target_altitude_ft",
            "target_velocity_fps",
            "target_heading_deg",
            "duration_s",
            "longitudinal_acceleration_g",
            "lateral_acceleration_g",
            "vertical_acceleration_g",
            "turn_direction",
        ],
    )

    wp1 = WP(
        maneuver_type=tew.ManeuverType.FLY_TO_POINT,
        target_range_nm=10.0,
        target_azimuth_deg=90.0,
        target_altitude_ft=1000.0,
        target_velocity_fps=0.0,
        target_heading_deg=0.0,
        duration_s=0.0,
        longitudinal_acceleration_g=0.0,
        lateral_acceleration_g=0.0,
        vertical_acceleration_g=0.0,
        turn_direction=tew.TurnDirection.LEFT,
    )

    wp2 = WP(
        maneuver_type=tew.ManeuverType.FLY_FOR_DURATION,
        target_range_nm=0,
        target_azimuth_deg=0,
        target_altitude_ft=0,
        target_velocity_fps=100.0,
        target_heading_deg=45.0,
        duration_s=12.5,
        longitudinal_acceleration_g=0.0,
        lateral_acceleration_g=0.0,
        vertical_acceleration_g=0.0,
        turn_direction=tew.TurnDirection.LEFT,
    )

    inst.waypoints = [wp1, wp2]
    inst._populate_waypoint_list()

    # Verify tree has two items inserted
    assert len(inst.wp_tree.items) == 2

    # Test _get_total_simulation_time by monkeypatching Target.generate_path_from_waypoints
    def fake_generate(wps, use_spline):
        return ([], 123.4)

    monkeypatch.setattr(tew.Target, "generate_path_from_waypoints", staticmethod(fake_generate))
    inst.use_spline_var = DummyVar(False)
    total = inst._get_total_simulation_time()
    assert total == 123.4
import pytest
import tkinter as tk
from target_simulator.gui.trajectory_editor_window import TrajectoryEditorWindow


def test_trajectory_editor_window_init():
    root = tk.Tk()
    win = TrajectoryEditorWindow(root, existing_ids=[], max_range_nm=100)
    assert win is not None
    root.destroy()
