import json
import os
import tempfile
import unittest
from unittest.mock import patch

from ezpanos.ezpanos import EzPanOS


class TestPanoramaEstateDiscovery(unittest.TestCase):
    def test_discover_panorama_managed_devices_enriches_groups_and_templates(self):
        client = EzPanOS(endpoint="192.0.2.10", api_key="test-key", connect_on_init=False)

        devices_payload = {
            "response": {
                "result": {
                    "devices": {
                        "entry": [
                            {
                                "serial": "SERIAL-1",
                                "hostname": "fw-a",
                                "ip-address": "10.0.0.5",
                                "connected": "yes",
                            },
                            {
                                "serial": "SERIAL-2",
                                "hostname": "fw-b",
                                "ip-address": "10.0.0.6",
                                "connected": "no",
                            },
                        ]
                    }
                }
            }
        }
        groups_payload = {
            "response": {
                "result": {
                    "devicegroups": {
                        "entry": [
                            {"name": "DG-EDGE", "devices": {"entry": [{"name": "SERIAL-1"}]}},
                            {"name": "DG-DMZ", "devices": {"entry": [{"name": "SERIAL-2"}]}},
                        ]
                    }
                }
            }
        }
        templates_payload = {
            "response": {
                "result": {
                    "templates": {
                        "entry": [
                            {"name": "TPL-A", "devices": {"entry": [{"name": "SERIAL-1"}]}},
                            {"name": "TPL-B", "devices": {"entry": [{"name": "SERIAL-2"}]}},
                        ]
                    },
                    "template-stack": {
                        "entry": [
                            {"name": "STACK-A", "devices": {"entry": [{"name": "SERIAL-2"}]}},
                        ]
                    },
                }
            }
        }
        system_info_payload = {
            "response": {
                "result": {
                    "system": {
                        "hostname": "pano-a",
                        "serial": "PAN-SERIAL-1",
                        "model": "Panorama",
                    }
                }
            }
        }

        payload_by_command = {
            "show system info": system_info_payload,
            "show devices all": devices_payload,
            "show devicegroups": groups_payload,
            "show templates": templates_payload,
        }

        with patch.object(client, "execute", side_effect=lambda command, **_: payload_by_command.get(command)):
            result = client.discover_panorama_managed_devices(include_templates=True, include_raw=False)

        self.assertEqual(result["summary"]["managed_device_count"], 2)
        self.assertEqual(result["summary"]["connected_managed_count"], 1)
        self.assertEqual(result["summary"]["device_group_count"], 2)
        self.assertEqual(result["summary"]["template_count"], 2)
        self.assertEqual(result["summary"]["template_stack_count"], 1)

        first = result["managed_devices"][0]
        second = result["managed_devices"][1]
        self.assertEqual(first["device_groups"], ["DG-EDGE"])
        self.assertEqual(first["templates"], ["TPL-A"])
        self.assertEqual(first["template_stacks"], [])
        self.assertEqual(second["device_groups"], ["DG-DMZ"])
        self.assertEqual(second["templates"], ["TPL-B"])
        self.assertEqual(second["template_stacks"], ["STACK-A"])

    def test_estate_instances_from_config_profile_keeps_unique_ids_for_overlapping_managed_ips(self):
        config = {
            "profiles": {
                "pano-estate": {
                    "username": "svc_panorama",
                    "password": "svc_password",
                    "endpoints": [
                        {
                            "endpoint": "198.51.100.10",
                            "discover_managed_devices": True,
                        },
                        {
                            "endpoint": "198.51.100.11",
                            "discover_managed_devices": True,
                        },
                    ]
                }
            }
        }

        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as handle:
            json.dump(config, handle)
            config_path = handle.name

        def fake_discovery(instance: EzPanOS, include_templates: bool = True, include_raw: bool = False):
            if instance.endpoint == "198.51.100.10":
                return {
                    "managed_devices": [
                        {
                            "serial": "SERIAL-A",
                            "hostname": "fw-shared-a",
                            "endpoint": "10.0.0.5",
                            "connected": True,
                            "device_groups": ["DG-A"],
                            "templates": ["TPL-A"],
                            "template_stacks": [],
                        }
                    ]
                }
            return {
                "managed_devices": [
                    {
                        "serial": "SERIAL-B",
                        "hostname": "fw-shared-b",
                        "endpoint": "10.0.0.5",
                        "connected": True,
                        "device_groups": ["DG-B"],
                        "templates": ["TPL-B"],
                        "template_stacks": [],
                    }
                ]
            }

        try:
            with patch.object(EzPanOS, "ensure_connected", return_value=True):
                with patch.object(EzPanOS, "request_api_key", return_value="test-key"):
                    with patch.object(EzPanOS, "discover_panorama_managed_devices", autospec=True, side_effect=fake_discovery):
                        instances = EzPanOS.estate_instances_from_config_profile(
                            config_path=config_path,
                            config_profile="pano-estate",
                            connect_on_init=False,
                            include_unavailable=True,
                            expand_panorama=True,
                            include_panorama_controllers=True,
                        )
        finally:
            os.unlink(config_path)

        self.assertEqual(len(instances), 4)
        estate_ids = [instance.estate_id for instance in instances]
        self.assertEqual(len(estate_ids), len(set(estate_ids)))

        managed = [instance for instance in instances if instance.estate_role == "panorama-managed-firewall"]
        self.assertEqual(len(managed), 2)
        self.assertEqual({instance.endpoint for instance in managed}, {"10.0.0.5"})
        self.assertEqual({instance.panorama_controller_endpoint for instance in managed}, {"198.51.100.10", "198.51.100.11"})
        self.assertEqual({instance.panorama_managed_serial for instance in managed}, {"SERIAL-A", "SERIAL-B"})


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