"""Unit-Tests für alle importierbaren Python-Kommandozeilenwerkzeuge."""

from __future__ import annotations

import importlib
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

ROOT = Path(__file__).resolve().parents[2]
TOOLS = ROOT / "tools" / "python"
sys.path.insert(0, str(TOOLS))


class RecordingClient:
    def __init__(self, get_result=None):
        self.calls = []
        self.get_result = get_result or {}

    def request(self, method, path, **kwargs):
        self.calls.append((method, path, kwargs))
        if method == "GET":
            return dict(self.get_result)
        return {"method": method, "path": path}


class CommandTests(unittest.TestCase):
    def module(self, name):
        return importlib.import_module(name)

    def run_with_client(self, name, argv, *, get_result=None, extra_patches=()):
        module = self.module(name)
        client = RecordingClient(get_result)
        patches = [patch.object(module, "client_from_args", return_value=client)]
        patches.extend(extra_patches)
        for current in patches:
            current.start()
            self.addCleanup(current.stop)
        result = module.main(argv)
        return result, client

    def test_create_and_append_commands(self):
        with tempfile.TemporaryDirectory() as directory:
            gpx = Path(directory) / "route.gpx"
            picture = Path(directory) / "picture.png"
            audio = Path(directory) / "audio.mp3"
            for file_path in (gpx, picture, audio):
                file_path.write_bytes(b"content")

            _, client = self.run_with_client("append_route", ["--route-id", "1", "--gpx", str(gpx)])
            self.assertEqual(client.calls[0][0:2], ("POST", "/api/routes/1/append"))

            _, client = self.run_with_client(
                "create_route",
                ["--gpx", str(gpx), "--name", "Route", "--slug", "route", "--description", "Text", "--school-name", "Schule"],
            )
            self.assertEqual(client.calls[0][1], "/api/routes")
            self.assertEqual(client.calls[0][2]["fields"]["schoolName"], "Schule")

            _, client = self.run_with_client("create_audio", ["--route-id", "1", "--poi-id", "2", "--file", str(audio)])
            self.assertIn("audio", client.calls[0][2]["files"])

            _, client = self.run_with_client(
                "create_picture",
                ["--route-id", "1", "--poi-id", "2", "--file", str(picture), "--caption", "Bild", "--sequence", "3"],
            )
            self.assertEqual(client.calls[0][2]["fields"], {"caption": "Bild", "sequence": 3})

            _, client = self.run_with_client(
                "create_poi",
                ["--route-id", "1", "--title", "POI", "--description", "Text", "--lat", "50.1", "--lon", "10.2", "--trigger-radius", "25", "--sequence", "0"],
            )
            self.assertEqual(client.calls[0][2]["json_body"]["sequence"], 0)
            self.assertEqual(client.calls[0][2]["json_body"]["description"], "Text")

    def test_delete_commands_cover_confirmation_and_requests(self):
        cases = [
            ("delete_route", ["--route-id", "1"], "/api/routes/1"),
            ("delete_poi", ["--route-id", "1", "--poi-id", "2"], "/api/routes/1/pois/2"),
            ("delete_picture", ["--route-id", "1", "--poi-id", "2", "--picture-id", "3"], "/api/routes/1/pois/2/pictures/3"),
            ("delete_audio", ["--route-id", "1", "--poi-id", "2"], "/api/routes/1/pois/2/audio"),
        ]
        for name, argv, path in cases:
            with self.subTest(name=name, confirmed=False):
                module = self.module(name)
                result, client = self.run_with_client(
                    name,
                    argv,
                    extra_patches=(patch.object(module, "confirm", return_value=False),),
                )
                self.assertEqual(result, {"cancelled": True})
                self.assertEqual(client.calls, [])
            with self.subTest(name=name, confirmed=True):
                module = self.module(name)
                _, client = self.run_with_client(
                    name,
                    argv,
                    extra_patches=(patch.object(module, "confirm", return_value=True),),
                )
                self.assertEqual(client.calls[0][0:2], ("DELETE", path))

    def test_restore_and_update_commands(self):
        _, client = self.run_with_client("restore_route", ["--route-id", "7"])
        self.assertEqual(client.calls[0][0:2], ("POST", "/api/routes/7/restore"))

        with tempfile.TemporaryDirectory() as directory:
            replacement = Path(directory) / "replacement.bin"
            replacement.write_bytes(b"content")

            _, client = self.run_with_client(
                "update_audio", ["--route-id", "1", "--poi-id", "2", "--file", str(replacement)]
            )
            self.assertEqual(client.calls[0][0], "PUT")

            _, client = self.run_with_client(
                "update_picture",
                ["--route-id", "1", "--poi-id", "2", "--picture-id", "3", "--file", str(replacement)],
                get_result={"caption": "Alt", "sequence": 4},
                extra_patches=(patch.object(self.module("update_picture"), "ask_with_default", side_effect=lambda label, current, cast=str: current),),
            )
            self.assertEqual([call[0] for call in client.calls], ["GET", "PUT"])
            self.assertIn("files", client.calls[1][2])

            module = self.module("update_picture")
            _, client = self.run_with_client(
                "update_picture",
                ["--route-id", "1", "--poi-id", "2", "--picture-id", "3"],
                get_result={"caption": "Alt", "sequence": 4},
                extra_patches=(
                    patch.object(module, "ask_path", return_value=None),
                    patch.object(module, "ask_with_default", side_effect=lambda label, current, cast=str: current),
                ),
            )
            self.assertIn("json_body", client.calls[1][2])

            _, client = self.run_with_client(
                "update_route",
                ["--route-id", "1", "--gpx", str(replacement)],
                get_result={"name": "Alt", "description": "Text", "schoolName": "Schule"},
                extra_patches=(patch.object(self.module("update_route"), "ask_with_default", side_effect=lambda label, current, cast=str: current),),
            )
            self.assertIn("files", client.calls[1][2])

            module = self.module("update_route")
            _, client = self.run_with_client(
                "update_route",
                ["--route-id", "1"],
                get_result={"name": "Alt", "description": "Text", "schoolName": "Schule"},
                extra_patches=(
                    patch.object(module, "ask_path", return_value=None),
                    patch.object(module, "ask_with_default", side_effect=lambda label, current, cast=str: current),
                ),
            )
            self.assertIn("json_body", client.calls[1][2])

        _, client = self.run_with_client(
            "update_poi",
            ["--route-id", "1", "--poi-id", "2"],
            get_result={
                "title": "Alt",
                "description": "Text",
                "lat": 50.0,
                "lon": 10.0,
                "triggerRadiusM": 25.0,
                "sequence": 1,
            },
            extra_patches=(patch.object(self.module("update_poi"), "ask_with_default", side_effect=lambda label, current, cast=str: current),),
        )
        self.assertEqual(client.calls[1][2]["json_body"]["title"], "Alt")


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