import json

from utils.exporters import TopologyExporter

SAMPLE_DATA = {
    'nodes': [
        {
            'id': '192.168.1.1',
            'label': 'gateway',
            'ip': '192.168.1.1',
            'type': 'gateway',
            'hostname': 'gateway',
            'state': 'up',
            'mac_address': 'AA:BB:CC:DD:EE:01',
            'vendor': 'Cisco',
            'open_ports': 0,
            'protocols': [],
        },
        {
            'id': '192.168.1.10',
            'label': 'web-server',
            'ip': '192.168.1.10',
            'type': 'server',
            'hostname': 'web-server',
            'state': 'up',
            'mac_address': 'AA:BB:CC:DD:EE:10',
            'vendor': 'Dell',
            'open_ports': 2,
            'protocols': ['tcp'],
        },
    ],
    'links': [
        {
            'source': '192.168.1.10',
            'target': '192.168.1.1',
            'weight': 1.0,
            'type': 'gateway',
        },
    ],
    'stats': {
        'total_nodes': 2,
        'total_links': 1,
        'node_types': {'gateway': 1, 'server': 1},
    },
}


class TestExportJSON:
    def test_valid_json(self):
        exporter = TopologyExporter()
        result = exporter.export_json(SAMPLE_DATA)
        parsed = json.loads(result)

        assert 'metadata' in parsed
        assert 'topology' in parsed

    def test_metadata_counts(self):
        exporter = TopologyExporter()
        result = exporter.export_json(SAMPLE_DATA)
        parsed = json.loads(result)

        assert parsed['metadata']['total_nodes'] == 2
        assert parsed['metadata']['total_links'] == 1

    def test_empty_data(self):
        exporter = TopologyExporter()
        result = exporter.export_json({'nodes': [], 'links': [], 'stats': {}})
        parsed = json.loads(result)

        assert parsed['metadata']['total_nodes'] == 0


class TestExportCSVNodes:
    def test_csv_header(self):
        exporter = TopologyExporter()
        result = exporter.export_csv_nodes(SAMPLE_DATA)
        lines = result.strip().splitlines()

        assert lines[0] == 'IP,Hostname,Type,MAC_Address,Vendor,Open_Ports,State'

    def test_csv_row_count(self):
        exporter = TopologyExporter()
        result = exporter.export_csv_nodes(SAMPLE_DATA)
        lines = result.strip().splitlines()

        assert len(lines) == 3  # header + 2 nodes

    def test_csv_content(self):
        exporter = TopologyExporter()
        result = exporter.export_csv_nodes(SAMPLE_DATA)

        assert '192.168.1.1' in result
        assert 'gateway' in result
        assert 'Cisco' in result


class TestExportCSVLinks:
    def test_csv_header(self):
        exporter = TopologyExporter()
        result = exporter.export_csv_links(SAMPLE_DATA)
        lines = result.strip().splitlines()

        assert lines[0] == 'Source,Target,Weight,Type'

    def test_csv_row_count(self):
        exporter = TopologyExporter()
        result = exporter.export_csv_links(SAMPLE_DATA)
        lines = result.strip().splitlines()

        assert len(lines) == 2  # header + 1 link

    def test_empty_links(self):
        exporter = TopologyExporter()
        result = exporter.export_csv_links({'links': []})
        lines = result.strip().splitlines()

        assert len(lines) == 1  # header only


class TestExportSVG:
    def test_produces_valid_svg(self):
        exporter = TopologyExporter()
        result = exporter.export_svg(SAMPLE_DATA)

        assert result.startswith('<svg')
        assert result.strip().endswith('</svg>')

    def test_contains_nodes(self):
        exporter = TopologyExporter()
        result = exporter.export_svg(SAMPLE_DATA)

        assert '192.168.1.1' in result
        assert '192.168.1.10' in result

    def test_contains_circles(self):
        exporter = TopologyExporter()
        result = exporter.export_svg(SAMPLE_DATA)

        assert '<circle' in result

    def test_contains_edges(self):
        exporter = TopologyExporter()
        result = exporter.export_svg(SAMPLE_DATA)

        assert '<line' in result

    def test_contains_legend(self):
        exporter = TopologyExporter()
        result = exporter.export_svg(SAMPLE_DATA)

        assert 'Gateway' in result
        assert 'Server' in result

    def test_empty_data(self):
        exporter = TopologyExporter()
        result = exporter.export_svg({'nodes': [], 'links': [], 'stats': {}})

        assert 'No hosts discovered' in result

    def test_escapes_special_chars(self):
        exporter = TopologyExporter()
        data = {
            'nodes': [{
                'id': '10.0.0.1',
                'label': '<script>alert(1)</script>',
                'ip': '10.0.0.1',
                'type': 'gateway',
            }],
            'links': [],
            'stats': {'total_nodes': 1, 'total_links': 0, 'node_types': {'gateway': 1}},
        }
        result = exporter.export_svg(data)

        assert '<script>' not in result
        assert '&lt;script&gt;' in result


class TestExportRaster:
    def test_png_magic_bytes(self):
        exporter = TopologyExporter()
        data = exporter._render_raster(SAMPLE_DATA, 400, 300, fmt='png')

        assert data[:4] == b'\x89PNG'

    def test_jpeg_magic_bytes(self):
        exporter = TopologyExporter()
        data = exporter._render_raster(SAMPLE_DATA, 400, 300, fmt='jpg')

        assert data[:2] == b'\xff\xd8'

    def test_png_nonempty(self):
        exporter = TopologyExporter()
        data = exporter._render_raster(SAMPLE_DATA, 400, 300, fmt='png')

        assert len(data) > 100

    def test_empty_data_png(self):
        exporter = TopologyExporter()
        empty = {'nodes': [], 'links': [], 'stats': {}}
        data = exporter._render_raster(empty, 400, 300, fmt='png')

        assert data[:4] == b'\x89PNG'


class TestExportImage:
    def test_svg_by_extension(self, tmp_path):
        exporter = TopologyExporter()
        path = str(tmp_path / 'out.svg')
        exporter.export_image(SAMPLE_DATA, path)

        with open(path, 'r') as f:
            content = f.read()
        assert content.startswith('<svg')

    def test_png_by_extension(self, tmp_path):
        exporter = TopologyExporter()
        path = str(tmp_path / 'out.png')
        exporter.export_image(SAMPLE_DATA, path)

        with open(path, 'rb') as f:
            assert f.read(4) == b'\x89PNG'

    def test_jpg_by_extension(self, tmp_path):
        exporter = TopologyExporter()
        path = str(tmp_path / 'out.jpg')
        exporter.export_image(SAMPLE_DATA, path)

        with open(path, 'rb') as f:
            assert f.read(2) == b'\xff\xd8'

    def test_jpeg_by_extension(self, tmp_path):
        exporter = TopologyExporter()
        path = str(tmp_path / 'out.jpeg')
        exporter.export_image(SAMPLE_DATA, path)

        with open(path, 'rb') as f:
            assert f.read(2) == b'\xff\xd8'

    def test_unsupported_extension(self):
        exporter = TopologyExporter()
        try:
            exporter.export_image(SAMPLE_DATA, 'out.bmp')
            assert False, "Should have raised ValueError"
        except ValueError as e:
            assert 'Unsupported' in str(e)


class TestGetExportFormats:
    def test_formats_list(self):
        exporter = TopologyExporter()
        formats = exporter.get_export_formats()

        assert 'json' in formats
        assert 'png' in formats
        assert 'jpg' in formats
        assert 'svg' in formats
        assert 'csv_nodes' in formats
        assert 'csv_links' in formats
