# (c) cavaliba.com


from io import StringIO

from django.core.management import call_command
from django.test import TestCase

import app_home.cache as cache
from app_user.models import SirenePermission
from tests import helper


class CommandTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_schema(classname="test1")
        helper.add_instance(classname="test1", keyname="test1-01")

    def test_check_command(self):

        output = StringIO()
        StringIO()
        call_command("check", verbosity=2, stdout=output)
        assert "System check identified no issues" in output.getvalue()

    def test_version_command(self):
        output = StringIO()
        StringIO()
        call_command("cavaliba", "version", verbosity=2, stdout=output)
        assert "." in output.getvalue()
        self.assertTrue(len(output.getvalue()) > 5)

    def test_start_command(self):
        output = StringIO()
        with self.assertLogs("app_home.migrator", level="INFO") as cm:
            call_command("cavaliba", "start", verbosity=2, stdout=output)
        assert any("start done" in msg for msg in cm.output)

    def test_schema_command(self):
        output = StringIO()
        StringIO()
        call_command("cavaliba", "schema", verbosity=2, stdout=output)
        assert "IAM" in output.getvalue()

    def test_get_command(self):
        output = StringIO()
        StringIO()
        call_command("cavaliba", "get", "--schema", "test1", "--yaml", verbosity=2, stdout=output)
        assert "keyname: test1-01" in output.getvalue()

    def test_get_command_csv(self):
        output = StringIO()
        call_command("cavaliba", "get", "--schema", "test1", "--csv", verbosity=2, stdout=output)
        content = output.getvalue()
        lines = content.strip().splitlines()
        header = lines[0].split(",")
        assert "keyname" in header
        assert "schema" in header
        assert "classname" not in header
        assert "id" not in header
        rows = [line.split(",") for line in lines[1:]]
        assert any("test1-01" in row for row in rows)

    def test_get_command_csv_field_filter(self):
        helper.add_schema(
            classname="test4",
            field_definition={
                "keep_me": {"dataformat": "string", "displayname": "Keep Me"},
                "drop_me": {"dataformat": "string", "displayname": "Drop Me"},
            },
        )
        helper.add_instance(
            classname="test4",
            keyname="test4-01",
            fields={"keep_me": "keepvalue", "drop_me": "dropvalue"},
        )

        output = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "test4",
            "--csv",
            "--field",
            "keep_me",
            verbosity=2,
            stdout=output,
        )
        content = output.getvalue()
        assert "keep_me" in content
        assert "keepvalue" in content
        assert "drop_me" not in content
        assert "dropvalue" not in content

    def test_get_command_yaml_includes_action_noop(self):
        output = StringIO()
        call_command("cavaliba", "get", "--schema", "test1", "--yaml", verbosity=2, stdout=output)
        assert "_action: noop" in output.getvalue()

    def test_get_command_csv_has_no_action_column(self):
        # unlike YAML/JSON, CSV never gets a safe-by-default "_action" column
        output = StringIO()
        call_command("cavaliba", "get", "--schema", "test1", "--csv", verbosity=2, stdout=output)
        lines = output.getvalue().strip().splitlines()
        header = lines[0].split(",")
        assert "_action" not in header

    def test_get_command_csv_with_key(self):
        output = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "test1",
            "--key",
            "test1-01",
            "--csv",
            verbosity=2,
            stdout=output,
        )
        content = output.getvalue()
        lines = content.strip().splitlines()
        assert len(lines) == 2
        header = lines[0].split(",")
        assert "keyname" in header
        assert "schema" in header
        row = lines[1].split(",")
        assert "test1-01" in row

    def test_get_command_csv_permission_drops_id_and_classname(self):
        SirenePermission.objects.create(keyname="p_test_perm", displayname="Test Perm")

        output = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "_permission",
            "--key",
            "p_test_perm",
            "--csv",
            verbosity=2,
            stdout=output,
        )
        content = output.getvalue()
        lines = content.strip().splitlines()
        header = lines[0].split(",")
        assert "keyname" in header
        assert "id" not in header
        assert "classname" not in header
        assert any("p_test_perm" in line for line in lines[1:])

    def test_get_command_csv_home_drops_classname(self):
        output = StringIO()
        call_command("cavaliba", "get", "--schema", "_home", "--csv", verbosity=2, stdout=output)
        content = output.getvalue()
        lines = content.strip().splitlines()
        if lines:
            header = lines[0].split(",")
            assert "classname" not in header
            assert "id" not in header

    def test_get_command_csv_custom_delimiter(self):
        output = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "test1",
            "--csv",
            "--delimiter",
            ";",
            verbosity=2,
            stdout=output,
        )
        content = output.getvalue()
        header = content.strip().splitlines()[0]
        assert ";" in header
        assert "," not in header

    def test_get_command_csv_invalid_delimiter(self):
        output = StringIO()
        error = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "test1",
            "--csv",
            "--delimiter",
            "::",
            verbosity=2,
            stdout=output,
            stderr=error,
        )
        assert "single character" in error.getvalue()
        assert output.getvalue() == ""

    def test_get_command_csv_custom_encoding(self):
        output = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "test1",
            "--csv",
            "--encoding",
            "latin-1",
            verbosity=2,
            stdout=output,
        )
        content = output.getvalue()
        assert "test1-01" in content

    def test_get_command_csv_unknown_encoding(self):
        output = StringIO()
        error = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "test1",
            "--csv",
            "--encoding",
            "not-a-real-encoding",
            verbosity=2,
            stdout=output,
            stderr=error,
        )
        assert "Unknown encoding" in error.getvalue()

    def test_get_command_field_whitelist(self):
        helper.add_schema(
            classname="test2",
            field_definition={
                "keep_me": {"dataformat": "string", "displayname": "Keep Me"},
                "drop_me": {"dataformat": "string", "displayname": "Drop Me"},
            },
        )
        helper.add_instance(
            classname="test2",
            keyname="test2-01",
            fields={"keep_me": "keepvalue", "drop_me": "dropvalue"},
        )

        output = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "test2",
            "--yaml",
            "--field",
            "keep_me",
            verbosity=2,
            stdout=output,
        )
        content = output.getvalue()
        assert "keepvalue" in content
        assert "dropvalue" not in content

    def test_get_command_field_blacklist(self):
        helper.add_schema(
            classname="test3",
            field_definition={
                "keep_me": {"dataformat": "string", "displayname": "Keep Me"},
                "drop_me": {"dataformat": "string", "displayname": "Drop Me"},
            },
        )
        helper.add_instance(
            classname="test3",
            keyname="test3-01",
            fields={"keep_me": "keepvalue", "drop_me": "dropvalue"},
        )

        output = StringIO()
        call_command(
            "cavaliba",
            "get",
            "--schema",
            "test3",
            "--yaml",
            "--field",
            "!drop_me",
            verbosity=2,
            stdout=output,
        )
        content = output.getvalue()
        assert "keepvalue" in content
        assert "dropvalue" not in content

    def test_conf_command(self):

        output = StringIO()
        StringIO()
        call_command("cavaliba", "conf", verbosity=2, stdout=output)
        assert "GLOBAL_APPNAME" in output.getvalue()

    def test_log_command(self):
        output = StringIO()
        StringIO()
        call_command("cavaliba", "log", verbosity=2, stdout=output)
        assert "entries" in output.getvalue()

    def test_log_purge_command(self):

        output = StringIO()
        StringIO()
        call_command("cavaliba", "log", "--log_purge", verbosity=2, stdout=output)
        assert "log done" in output.getvalue()

    def test_load_command_unknown(self):

        output = StringIO()
        err = StringIO()
        call_command("cavaliba", "load", "unknown_file", verbosity=2, stdout=output, stderr=err)
        assert "load done" in output.getvalue()
        assert "SKIP - Unknown filename" in err.getvalue()
