import os
import unittest
import tempfile
import json
from pathlib import Path
from unittest.mock import patch
from armorer.config import ConfigManager

class TestConfigManager(unittest.TestCase):
    def setUp(self):
        self.env_path = "test_env"
        if os.path.exists(self.env_path):
            os.remove(self.env_path)
        self.tmpdir = tempfile.TemporaryDirectory()
        self.vault_file = Path(self.tmpdir.name) / "vault.json"
        self.config_dir = Path(self.tmpdir.name)
        self.patch_vault = patch.object(ConfigManager, "VAULT_FILE", self.vault_file)
        self.patch_config_dir = patch.object(ConfigManager, "CONFIG_DIR", self.config_dir)
        self.patch_vault.start()
        self.patch_config_dir.start()

    def tearDown(self):
        if os.path.exists(self.env_path):
            os.remove(self.env_path)
        self.patch_vault.stop()
        self.patch_config_dir.stop()
        self.tmpdir.cleanup()

    @patch("armorer.config.platform.system")
    def test_headless_detection(self, mock_system):
        """Test that keyring is disabled on headless Linux."""
        mock_system.return_value = "Linux"
        
        # Safe way to simulate no DISPLAY without clearing PATH etc.
        with patch.dict(os.environ):
            if "DISPLAY" in os.environ:
                del os.environ["DISPLAY"]
            
            config = ConfigManager(env_path=self.env_path)
            self.assertFalse(config.use_keyring, "Keyring should be disabled on headless Linux")

    @patch("armorer.config.platform.system")
    def test_display_detection(self, mock_system):
        """Test that keyring is enabled on Linux with DISPLAY."""
        mock_system.return_value = "Linux"
        
        # Ensure DISPLAY is present
        with patch.dict(os.environ, {"DISPLAY": ":0"}):
            config = ConfigManager(env_path=self.env_path)
            self.assertTrue(config.use_keyring, "Keyring should be enabled on Linux with DISPLAY")

    @patch("armorer.config.keyring")
    @patch("armorer.config.platform.system")
    def test_set_key_headless(self, mock_system, mock_keyring):
        """Test secret set_key stores in vault and skips keyring in headless mode."""
        mock_system.return_value = "Linux"
        
        with patch.dict(os.environ):
            if "DISPLAY" in os.environ:
                del os.environ["DISPLAY"]
            
            config = ConfigManager(env_path=self.env_path)
            config.set_key("TEST_SECRET_KEY", "secret_value")
            
            # Verify keyring was NOT used
            mock_keyring.set_password.assert_not_called()
            
            # Verify secret not persisted in .env
            env_content = Path(self.env_path).read_text()
            self.assertNotIn("TEST_SECRET_KEY=secret_value", env_content)

            # Verify persisted in vault
            vault = json.loads(self.vault_file.read_text())
            self.assertEqual(vault["secrets"]["TEST_SECRET_KEY"], "secret_value")

    @patch("armorer.config.keyring")
    @patch("armorer.config.platform.system")
    def test_set_key_headful(self, mock_system, mock_keyring):
        """Test set_key uses keyring and vault for secrets; not .env."""
        mock_system.return_value = "Linux"
    
        with patch.dict(os.environ, {"DISPLAY": ":0"}):
            config = ConfigManager(env_path=self.env_path)
            config.set_key("TEST_SECRET_KEY", "secret_value")
    
            # Verify keyring WAS used
            mock_keyring.set_password.assert_called_with("armorer", "TEST_SECRET_KEY", "secret_value")
    
            # Verify not written to .env
            env_content = Path(self.env_path).read_text()
            self.assertNotIn("TEST_SECRET_KEY=secret_value", env_content)

            # Verify in vault
            vault = json.loads(self.vault_file.read_text())
            self.assertEqual(vault["secrets"]["TEST_SECRET_KEY"], "secret_value")

    @patch("armorer.config.platform.system")
    def test_non_secret_still_uses_env(self, mock_system):
        mock_system.return_value = "Linux"
        with patch.dict(os.environ, {"DISPLAY": ":0"}):
            config = ConfigManager(env_path=self.env_path)
            config.set_key("MODEL_PROVIDER", "openai")
            env_content = Path(self.env_path).read_text()
            self.assertIn("MODEL_PROVIDER=openai", env_content)

    @patch("armorer.config.platform.system")
    def test_migrate_env_secret_to_vault(self, mock_system):
        mock_system.return_value = "Linux"
        Path(self.env_path).write_text("TELEGRAM_BOT_TOKEN=test-token\nMODEL_PROVIDER=openai\n")

        with patch.dict(os.environ, {"DISPLAY": ":0"}):
            config = ConfigManager(env_path=self.env_path)

        env_content = Path(self.env_path).read_text()
        self.assertNotIn("TELEGRAM_BOT_TOKEN=test-token", env_content)
        self.assertIn("MODEL_PROVIDER=openai", env_content)
        self.assertEqual(config.get_key("TELEGRAM_BOT_TOKEN"), "test-token")

    @patch("armorer.config.platform.system")
    def test_armorer_config_dir_env_takes_precedence_over_legacy_config_dir(self, mock_system):
        mock_system.return_value = "Linux"
        armorer_config_dir = self.config_dir / "armorer-env"
        legacy_config_dir = self.config_dir / "legacy-env"

        with patch.dict(
            os.environ,
            {
                "DISPLAY": ":0",
                "ARMORER_CONFIG_DIR": str(armorer_config_dir),
                "CONFIG_DIR": str(legacy_config_dir),
            },
        ):
            config = ConfigManager(env_path=self.env_path)

        self.assertEqual(config.CONFIG_DIR, armorer_config_dir)
        self.assertEqual(config.VAULT_FILE, armorer_config_dir / "vault.json")
        self.assertTrue(config.VAULT_FILE.exists())
        self.assertFalse((legacy_config_dir / "vault.json").exists())


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