# (c) cavaliba.com - tests / app_data.ipam.subnet_ip_list_csv

import io

from django.test import TestCase, override_settings

import app_home.cache as cache
from app_data.ipam import subnet_ip_list_csv
from app_home.configuration import set_configuration
from tests import helper

HEADER = "ip,collected,description,referrers"


class IpamIpListCsvTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_schema(
            classname="ipcsvtest",
            field_definition={"ip_address": {"dataformat": "ipv4", "displayname": "IP"}},
        )

    def _write(self, cidr="203.0.113.0/24", **kwargs):
        out = io.StringIO()
        count = subnet_ip_list_csv(cidr, out, **kwargs)
        return count, out.getvalue()

    def _add_ips(self, lasts):
        for last in lasts:
            helper.add_instance(
                classname="ipam_ip",
                fields={"displayname": f"203.0.113.{last}", "description": f"desc{last}"},
            )

    def test_header_written_even_for_an_empty_subnet(self):
        count, text = self._write()

        self.assertEqual(count, 0)
        self.assertEqual(text.splitlines(), [HEADER])

    def test_header_written_for_an_invalid_cidr(self):
        count, text = self._write(cidr="not-a-cidr")

        self.assertEqual(count, 0)
        self.assertEqual(text.splitlines(), [HEADER])

    def test_one_row_per_ip_ordered_by_keyname(self):
        self._add_ips([30, 10, 20])

        count, text = self._write()

        self.assertEqual(count, 3)
        self.assertEqual(
            text.splitlines(),
            [
                HEADER,
                "203.0.113.10,no,desc10,",
                "203.0.113.20,no,desc20,",
                "203.0.113.30,no,desc30,",
            ],
        )

    def test_collected_rendered_as_yes_no(self):
        helper.add_instance(
            classname="ipam_ip", fields={"displayname": "203.0.113.10", "collected": True}
        )
        helper.add_instance(classname="ipam_ip", fields={"displayname": "203.0.113.20"})

        count, text = self._write()

        self.assertEqual(count, 2)
        self.assertIn("203.0.113.10,yes,,", text)
        self.assertIn("203.0.113.20,no,,", text)

    def test_referrers_joined_on_a_single_row_per_ip(self):
        # the HTML table repeats an IP once per referrer; the CSV must not
        helper.add_instance(
            classname="ipcsvtest", keyname="host01", fields={"ip_address": "203.0.113.10"}
        )
        helper.add_instance(
            classname="ipcsvtest", keyname="host02", fields={"ip_address": "203.0.113.10"}
        )

        count, text = self._write()

        self.assertEqual(count, 1)
        self.assertEqual(len(text.splitlines()), 2)
        row = text.splitlines()[1]
        self.assertIn("ipcsvtest:host01", row)
        self.assertIn("ipcsvtest:host02", row)

    def test_excludes_ips_outside_the_subnet(self):
        self._add_ips([10])
        helper.add_instance(classname="ipam_ip", fields={"displayname": "198.51.100.10"})

        count, text = self._write()

        self.assertEqual(count, 1)
        self.assertNotIn("198.51.100.10", text)

    def test_not_gated_on_subnet_size(self):
        self._add_ips([10])

        count, text = self._write(cidr="0.0.0.0/0")

        self.assertEqual(count, 1)
        self.assertIn("203.0.113.10", text)

    def test_capped_at_max_rows_with_a_truncation_note(self):
        self._add_ips([10, 20, 30])

        count, text = self._write(max_rows=2)

        self.assertEqual(count, 2)
        self.assertEqual(text.splitlines()[-1], "# truncated at 2 rows")
        self.assertNotIn("203.0.113.30", text)

    def test_no_truncation_note_when_the_cap_is_not_reached(self):
        self._add_ips([10, 20])

        count, text = self._write(max_rows=2)

        self.assertEqual(count, 2)
        self.assertNotIn("# truncated", text)

    @override_settings(CAVALIBA_IPAM_IP_LIST_FILE_MAX=1)
    def test_max_rows_defaults_to_setting(self):
        self._add_ips([10, 20])

        count, text = self._write()

        self.assertEqual(count, 1)
        self.assertIn("# truncated at 1 rows", text)

    def test_batching_does_not_change_the_output(self):
        # every chunk resumes from the last keyname written, so a small batch_size
        # must produce exactly the same rows as a single pass
        self._add_ips([10, 20, 30, 40, 50])

        count_single, text_single = self._write(batch_size=100)
        count_batched, text_batched = self._write(batch_size=2)

        self.assertEqual(count_single, 5)
        self.assertEqual(count_batched, 5)
        self.assertEqual(text_single, text_batched)

    def test_batching_with_a_cap_stops_at_max_rows(self):
        self._add_ips([10, 20, 30, 40, 50])

        count, text = self._write(max_rows=3, batch_size=2)

        self.assertEqual(count, 3)
        self.assertIn("# truncated at 3 rows", text)
        self.assertNotIn("203.0.113.40", text)

    def test_delimiter_comes_from_configuration(self):
        set_configuration(
            appname="home", keyname="CSV_DELIMITER",
            value=";", description="", page="", order=100,
        )  # fmt: skip
        cache.clear()
        self._add_ips([10])

        count, text = self._write()

        self.assertEqual(count, 1)
        self.assertIn("ip;collected;description;referrers", text)
        self.assertIn("203.0.113.10;no;desc10;", text)
