"""Unit tests for OpenSourceMalware configuration and token resolution."""

import os
import tempfile
import unittest
from unittest.mock import MagicMock
from cli_config import get_osm_token, apply_osm_overrides
from constants import Constants


class TestOpenSourceMalwareTokenResolution(unittest.TestCase):
    """Test cases for OpenSourceMalware token resolution."""

    def setUp(self):
        """Set up test fixtures."""
        # Clear any existing token
        if hasattr(Constants, "OSM_API_TOKEN"):
            Constants.OSM_API_TOKEN = None  # type: ignore[attr-defined]
        if hasattr(Constants, "OSM_ENABLED"):
            Constants.OSM_ENABLED = False  # type: ignore[attr-defined]
        if hasattr(Constants, "OSM_TOKEN_COMMAND"):
            Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]
        # Clear environment variables
        os.environ.pop("DEPGATE_OSM_API_TOKEN", None)
        # Note: DEPGATE_OSM_TOKEN_COMMAND env var is no longer supported for security

    def tearDown(self):
        """Clean up after tests."""
        # Clear environment variables
        os.environ.pop("DEPGATE_OSM_API_TOKEN", None)
        # Note: DEPGATE_OSM_TOKEN_COMMAND env var is no longer supported for security
        if hasattr(Constants, "OSM_API_TOKEN"):
            Constants.OSM_API_TOKEN = None  # type: ignore[attr-defined]
        if hasattr(Constants, "OSM_ENABLED"):
            Constants.OSM_ENABLED = False  # type: ignore[attr-defined]
        if hasattr(Constants, "OSM_TOKEN_COMMAND"):
            Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_get_token_from_env_var(self):
        """Test getting token from environment variable."""
        os.environ["DEPGATE_OSM_API_TOKEN"] = "test_token_from_env"
        token = get_osm_token()
        self.assertEqual(token, "test_token_from_env")

    def test_get_token_from_env_var_whitespace(self):
        """Test that whitespace is stripped from env var token."""
        os.environ["DEPGATE_OSM_API_TOKEN"] = "  test_token  \n"
        token = get_osm_token()
        self.assertEqual(token, "test_token")

    def test_get_token_from_command_yaml_config(self):
        """Test getting token from command via YAML config (trusted source)."""
        # Create a temporary file with token
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("test_token_from_command\n")
            temp_file = f.name

        try:
            # Set via Constants (simulating YAML config load)
            Constants.OSM_TOKEN_COMMAND = f"cat {temp_file}"  # type: ignore[attr-defined]
            token = get_osm_token()
            self.assertEqual(token, "test_token_from_command")
        finally:
            os.unlink(temp_file)
            if hasattr(Constants, "OSM_TOKEN_COMMAND"):
                Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_get_token_from_command_whitespace(self):
        """Test that whitespace is stripped from command output."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("  test_token  \n")
            temp_file = f.name

        try:
            # Set via Constants (simulating YAML config load)
            Constants.OSM_TOKEN_COMMAND = f"cat {temp_file}"  # type: ignore[attr-defined]
            token = get_osm_token()
            self.assertEqual(token, "test_token")
        finally:
            os.unlink(temp_file)
            if hasattr(Constants, "OSM_TOKEN_COMMAND"):
                Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_get_token_from_command_failure(self):
        """Test handling of command failure."""
        # Set via Constants (simulating YAML config load)
        Constants.OSM_TOKEN_COMMAND = "nonexistent_command_xyz"  # type: ignore[attr-defined]
        token = get_osm_token()
        # Should return None when command fails
        self.assertIsNone(token)
        if hasattr(Constants, "OSM_TOKEN_COMMAND"):
            Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_get_token_from_command_empty_output(self):
        """Test handling of command with empty output."""
        # Set via Constants (simulating YAML config load)
        Constants.OSM_TOKEN_COMMAND = "echo ''"  # type: ignore[attr-defined]
        token = get_osm_token()
        # Should return None when output is empty
        self.assertIsNone(token)
        if hasattr(Constants, "OSM_TOKEN_COMMAND"):
            Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_get_token_from_command_nonzero_exit(self):
        """Test handling of command with non-zero exit code."""
        # Set via Constants (simulating YAML config load)
        Constants.OSM_TOKEN_COMMAND = "false"  # type: ignore[attr-defined]
        token = get_osm_token()
        # Should return None when command fails
        self.assertIsNone(token)
        if hasattr(Constants, "OSM_TOKEN_COMMAND"):
            Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_get_token_from_constants(self):
        """Test getting token from Constants (YAML config)."""
        Constants.OSM_API_TOKEN = "test_token_from_config"  # type: ignore[attr-defined]
        token = get_osm_token()
        self.assertEqual(token, "test_token_from_config")

    def test_get_token_priority_env_var_over_command(self):
        """Test that environment variable takes priority over command."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("token_from_command\n")
            temp_file = f.name

        try:
            os.environ["DEPGATE_OSM_API_TOKEN"] = "token_from_env"
            # Set via Constants (simulating YAML config load)
            Constants.OSM_TOKEN_COMMAND = f"cat {temp_file}"  # type: ignore[attr-defined]
            token = get_osm_token()
            # Environment variable should win
            self.assertEqual(token, "token_from_env")
        finally:
            os.unlink(temp_file)
            if hasattr(Constants, "OSM_TOKEN_COMMAND"):
                Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_get_token_priority_env_var_over_constants(self):
        """Test that environment variable takes priority over Constants."""
        Constants.OSM_API_TOKEN = "token_from_config"  # type: ignore[attr-defined]
        os.environ["DEPGATE_OSM_API_TOKEN"] = "token_from_env"
        token = get_osm_token()
        # Environment variable should win
        self.assertEqual(token, "token_from_env")

    def test_get_token_priority_command_over_constants(self):
        """Test that command (from YAML config) takes priority over Constants token."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("token_from_command\n")
            temp_file = f.name

        try:
            Constants.OSM_API_TOKEN = "token_from_config"  # type: ignore[attr-defined]
            # Set via Constants (simulating YAML config load)
            Constants.OSM_TOKEN_COMMAND = f"cat {temp_file}"  # type: ignore[attr-defined]
            token = get_osm_token()
            # Command should win over Constants token
            self.assertEqual(token, "token_from_command")
        finally:
            os.unlink(temp_file)
            if hasattr(Constants, "OSM_TOKEN_COMMAND"):
                Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_get_token_none_when_all_missing(self):
        """Test that None is returned when no token sources are available."""
        token = get_osm_token()
        self.assertIsNone(token)


class TestOpenSourceMalwareCLIOverrides(unittest.TestCase):
    """Test cases for OpenSourceMalware CLI overrides."""

    def setUp(self):
        """Set up test fixtures."""
        # Clear any existing settings
        if hasattr(Constants, "OSM_API_TOKEN"):
            Constants.OSM_API_TOKEN = None  # type: ignore[attr-defined]
        if hasattr(Constants, "OSM_ENABLED"):
            Constants.OSM_ENABLED = False  # type: ignore[attr-defined]
        if hasattr(Constants, "OSM_TOKEN_COMMAND"):
            Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]
        # Clear environment variables
        os.environ.pop("DEPGATE_OSM_API_TOKEN", None)
        # Note: DEPGATE_OSM_TOKEN_COMMAND env var is no longer supported for security

    def tearDown(self):
        """Clean up after tests."""
        # Clear environment variables
        os.environ.pop("DEPGATE_OSM_API_TOKEN", None)
        # Note: DEPGATE_OSM_TOKEN_COMMAND env var is no longer supported for security
        if hasattr(Constants, "OSM_API_TOKEN"):
            Constants.OSM_API_TOKEN = None  # type: ignore[attr-defined]
        if hasattr(Constants, "OSM_ENABLED"):
            Constants.OSM_ENABLED = False  # type: ignore[attr-defined]
        if hasattr(Constants, "OSM_TOKEN_COMMAND"):
            Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_cli_token_direct(self):
        """Test CLI token provided directly via --osm-api-token."""
        args = MagicMock()
        args.OSM_DISABLE = False
        args.OSM_API_TOKEN = "test_cli_token"
        args.OSM_TOKEN_COMMAND = None
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = None
        args.OSM_MAX_RETRIES = None

        apply_osm_overrides(args)

        self.assertEqual(Constants.OSM_API_TOKEN, "test_cli_token")  # type: ignore[attr-defined]
        self.assertTrue(Constants.OSM_ENABLED)  # type: ignore[attr-defined]

    def test_cli_token_command(self):
        """Test CLI token command via --osm-token-command."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("test_token_from_cli_command\n")
            temp_file = f.name

        try:
            args = MagicMock()
            args.OSM_DISABLE = False
            args.OSM_API_TOKEN = None
            args.OSM_TOKEN_COMMAND = f"cat {temp_file}"
            args.OSM_BASE_URL = None
            args.OSM_CACHE_TTL = None
            args.OSM_AUTH_METHOD = None
            args.OSM_MAX_RETRIES = None

            apply_osm_overrides(args)

            self.assertEqual(Constants.OSM_API_TOKEN, "test_token_from_cli_command")  # type: ignore[attr-defined]
            self.assertTrue(Constants.OSM_ENABLED)  # type: ignore[attr-defined]
        finally:
            os.unlink(temp_file)

    def test_cli_token_command_priority_over_env(self):
        """Test that CLI token command takes priority over environment variable."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("token_from_cli_command\n")
            temp_file = f.name

        try:
            os.environ["DEPGATE_OSM_API_TOKEN"] = "token_from_env"
            args = MagicMock()
            args.OSM_DISABLE = False
            args.OSM_API_TOKEN = None
            args.OSM_TOKEN_COMMAND = f"cat {temp_file}"
            args.OSM_BASE_URL = None
            args.OSM_CACHE_TTL = None
            args.OSM_AUTH_METHOD = None
            args.OSM_MAX_RETRIES = None

            apply_osm_overrides(args)

            # CLI command should win over env var
            self.assertEqual(Constants.OSM_API_TOKEN, "token_from_cli_command")  # type: ignore[attr-defined]
            self.assertTrue(Constants.OSM_ENABLED)  # type: ignore[attr-defined]
        finally:
            os.unlink(temp_file)

    def test_cli_token_direct_priority_over_command(self):
        """Test that CLI direct token takes priority over CLI command."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("token_from_command\n")
            temp_file = f.name

        try:
            args = MagicMock()
            args.OSM_DISABLE = False
            args.OSM_API_TOKEN = "token_from_cli_direct"
            args.OSM_TOKEN_COMMAND = f"cat {temp_file}"
            args.OSM_BASE_URL = None
            args.OSM_CACHE_TTL = None
            args.OSM_AUTH_METHOD = None
            args.OSM_MAX_RETRIES = None

            apply_osm_overrides(args)

            # CLI direct token should win
            self.assertEqual(Constants.OSM_API_TOKEN, "token_from_cli_direct")  # type: ignore[attr-defined]
            self.assertTrue(Constants.OSM_ENABLED)  # type: ignore[attr-defined]
        finally:
            os.unlink(temp_file)

    def test_cli_token_command_failure(self):
        """Test handling of CLI token command failure."""
        args = MagicMock()
        args.OSM_DISABLE = False
        args.OSM_API_TOKEN = None
        args.OSM_TOKEN_COMMAND = "nonexistent_command_xyz"
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = None
        args.OSM_MAX_RETRIES = None

        apply_osm_overrides(args)

        # Should fall back to other sources or disable
        # Since no other sources available, should be disabled
        self.assertFalse(Constants.OSM_ENABLED)  # type: ignore[attr-defined]

    def test_cli_token_command_empty_output(self):
        """Test handling of CLI token command with empty output."""
        args = MagicMock()
        args.OSM_DISABLE = False
        args.OSM_API_TOKEN = None
        args.OSM_TOKEN_COMMAND = "echo ''"
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = None
        args.OSM_MAX_RETRIES = None

        apply_osm_overrides(args)

        # Should fall back to other sources or disable
        self.assertFalse(Constants.OSM_ENABLED)  # type: ignore[attr-defined]

    def test_cli_disable(self):
        """Test CLI disable flag."""
        args = MagicMock()
        args.OSM_DISABLE = True
        args.OSM_API_TOKEN = "test_token"
        args.OSM_TOKEN_COMMAND = None
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = None
        args.OSM_MAX_RETRIES = None

        apply_osm_overrides(args)

        self.assertFalse(Constants.OSM_ENABLED)  # type: ignore[attr-defined]

    def test_cli_disable_overrides_token(self):
        """Test that --osm-disable works even when API token is available."""
        # Set up environment with token
        os.environ["DEPGATE_OSM_API_TOKEN"] = "test_token_available"
        Constants.OSM_ENABLED = True  # type: ignore[attr-defined]
        Constants.OSM_API_TOKEN = "test_token_available"  # type: ignore[attr-defined]

        args = MagicMock()
        args.OSM_DISABLE = True
        args.OSM_API_TOKEN = None
        args.OSM_TOKEN_COMMAND = None
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = None
        args.OSM_MAX_RETRIES = None

        apply_osm_overrides(args)

        # Disable flag should override token availability
        self.assertFalse(Constants.OSM_ENABLED)  # type: ignore[attr-defined]

    def test_cli_disable_overrides_cli_token(self):
        """Test that --osm-disable works even when CLI token is provided."""
        args = MagicMock()
        args.OSM_DISABLE = True
        args.OSM_API_TOKEN = "test_cli_token"
        args.OSM_TOKEN_COMMAND = None
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = None
        args.OSM_MAX_RETRIES = None

        apply_osm_overrides(args)

        # Disable flag should override CLI token
        self.assertFalse(Constants.OSM_ENABLED)  # type: ignore[attr-defined]

    def test_cli_disable_overrides_token_command(self):
        """Test that --osm-disable works even when token command is provided."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("test_token_from_command\n")
            temp_file = f.name

        try:
            args = MagicMock()
            args.OSM_DISABLE = True
            args.OSM_API_TOKEN = None
            args.OSM_TOKEN_COMMAND = f"cat {temp_file}"
            args.OSM_BASE_URL = None
            args.OSM_CACHE_TTL = None
            args.OSM_AUTH_METHOD = None
            args.OSM_MAX_RETRIES = None

            apply_osm_overrides(args)

            # Disable flag should override token command
            self.assertFalse(Constants.OSM_ENABLED)  # type: ignore[attr-defined]
        finally:
            os.unlink(temp_file)

    def test_cli_fallback_to_env_var(self):
        """Test that CLI falls back to environment variable when no CLI token."""
        os.environ["DEPGATE_OSM_API_TOKEN"] = "token_from_env"
        args = MagicMock()
        args.OSM_DISABLE = False
        args.OSM_API_TOKEN = None
        args.OSM_TOKEN_COMMAND = None
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = None
        args.OSM_MAX_RETRIES = None

        apply_osm_overrides(args)

        self.assertEqual(Constants.OSM_API_TOKEN, "token_from_env")  # type: ignore[attr-defined]
        self.assertTrue(Constants.OSM_ENABLED)  # type: ignore[attr-defined]

    def test_cli_fallback_to_command_yaml_config(self):
        """Test that CLI falls back to YAML config command (trusted source)."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write("token_from_yaml_command\n")
            temp_file = f.name

        try:
            # Set via Constants (simulating YAML config load)
            Constants.OSM_TOKEN_COMMAND = f"cat {temp_file}"  # type: ignore[attr-defined]
            args = MagicMock()
            args.OSM_DISABLE = False
            args.OSM_API_TOKEN = None
            args.OSM_TOKEN_COMMAND = None
            args.OSM_BASE_URL = None
            args.OSM_CACHE_TTL = None
            args.OSM_AUTH_METHOD = None
            args.OSM_MAX_RETRIES = None

            apply_osm_overrides(args)

            self.assertEqual(Constants.OSM_API_TOKEN, "token_from_yaml_command")  # type: ignore[attr-defined]
            self.assertTrue(Constants.OSM_ENABLED)  # type: ignore[attr-defined]
        finally:
            os.unlink(temp_file)
            if hasattr(Constants, "OSM_TOKEN_COMMAND"):
                Constants.OSM_TOKEN_COMMAND = None  # type: ignore[attr-defined]

    def test_cli_other_overrides(self):
        """Test other CLI overrides (base_url, cache_ttl, etc.)."""
        args = MagicMock()
        args.OSM_DISABLE = False
        args.OSM_API_TOKEN = "test_token"
        args.OSM_TOKEN_COMMAND = None
        args.OSM_BASE_URL = "https://custom-api.example.com"
        args.OSM_CACHE_TTL = 7200
        args.OSM_AUTH_METHOD = "query"
        args.OSM_MAX_RETRIES = 10

        apply_osm_overrides(args)

        self.assertEqual(Constants.OSM_API_BASE_URL, "https://custom-api.example.com")  # type: ignore[attr-defined]
        self.assertEqual(Constants.OSM_CACHE_TTL_SEC, 7200)  # type: ignore[attr-defined]
        self.assertEqual(Constants.OSM_AUTH_METHOD, "query")  # type: ignore[attr-defined]
        self.assertEqual(Constants.OSM_MAX_RETRIES, 10)  # type: ignore[attr-defined]

    def test_cli_auth_method_validation(self):
        """Test that invalid auth methods are rejected."""
        args = MagicMock()
        args.OSM_DISABLE = False
        args.OSM_API_TOKEN = "test_token"
        args.OSM_TOKEN_COMMAND = None
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = "invalid_method"
        args.OSM_MAX_RETRIES = None

        # Store original value
        original_auth_method = getattr(Constants, "OSM_AUTH_METHOD", "header")

        apply_osm_overrides(args)

        # Invalid method should not change the value
        self.assertEqual(Constants.OSM_AUTH_METHOD, original_auth_method)  # type: ignore[attr-defined]

    def test_cli_no_token_disables(self):
        """Test that OSM is disabled when no token is available."""
        args = MagicMock()
        args.OSM_DISABLE = False
        args.OSM_API_TOKEN = None
        args.OSM_TOKEN_COMMAND = None
        args.OSM_BASE_URL = None
        args.OSM_CACHE_TTL = None
        args.OSM_AUTH_METHOD = None
        args.OSM_MAX_RETRIES = None

        apply_osm_overrides(args)

        self.assertFalse(Constants.OSM_ENABLED)  # type: ignore[attr-defined]


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