"""Unit tests for ConfigWriter."""

import os
import tempfile
from pathlib import Path
from unittest import TestCase
from unittest.mock import MagicMock, patch

import yaml

from flow._internal.init.writer import ConfigWriter
from flow.api.models import ValidationResult


class TestConfigWriter(TestCase):
    """Test configuration persistence."""

    def setUp(self):
        """Set up test fixtures."""
        self.temp_dir = tempfile.mkdtemp()
        self.config_path = Path(self.temp_dir) / "config.yaml"
        self.mock_keychain = MagicMock()
        self.writer = ConfigWriter(
            keychain=self.mock_keychain,
            config_path=self.config_path
        )

    def tearDown(self):
        """Clean up test fixtures."""
        import shutil
        shutil.rmtree(self.temp_dir, ignore_errors=True)

    def test_write_config_and_keychain(self):
        """Test writing config to file and API key to keychain."""
        config = {
            "api_key": "secret_key",
            "project": "test_project",
            "region": "us-central1-a",
            "api_url": "https://api.test.com"
        }
        validation = ValidationResult(is_valid=True, projects=[])

        self.writer.write(config, validation)

        # Check keychain was called
        self.mock_keychain.set_password.assert_called_once_with(
            "com.mlfoundry.flow",
            "api_key",
            "secret_key"
        )

        # Check file was written (without api_key)
        self.assertTrue(self.config_path.exists())
        with open(self.config_path) as f:
            saved_config = yaml.safe_load(f)

        self.assertNotIn("api_key", saved_config)
        self.assertEqual(saved_config["project"], "test_project")
        self.assertEqual(saved_config["region"], "us-central1-a")
        self.assertEqual(saved_config["api_url"], "https://api.test.com")

        # Check file permissions (Unix only)
        if os.name != 'nt':
            stat_info = self.config_path.stat()
            self.assertEqual(stat_info.st_mode & 0o777, 0o600)

    def test_write_no_api_key(self):
        """Test writing config without API key."""
        config = {
            "project": "test_project",
            "region": "us-central1-a"
        }
        validation = ValidationResult(is_valid=True, projects=[])

        self.writer.write(config, validation)

        # Keychain should not be called
        self.mock_keychain.set_password.assert_not_called()

        # File should be written
        self.assertTrue(self.config_path.exists())
        with open(self.config_path) as f:
            saved_config = yaml.safe_load(f)

        self.assertEqual(saved_config["project"], "test_project")
        self.assertEqual(saved_config["region"], "us-central1-a")

    def test_keychain_failure_continues(self):
        """Test that keychain failure doesn't prevent config write."""
        config = {
            "api_key": "secret_key",
            "project": "test_project"
        }
        validation = ValidationResult(is_valid=True, projects=[])

        # Mock keychain failure
        self.mock_keychain.set_password.side_effect = Exception("Keychain error")

        # Capture stderr
        with patch('sys.stderr') as mock_stderr:
            self.writer.write(config, validation)

        # Should still write config file
        self.assertTrue(self.config_path.exists())
        with open(self.config_path) as f:
            saved_config = yaml.safe_load(f)

        self.assertEqual(saved_config["project"], "test_project")

    def test_read_api_key(self):
        """Test reading API key from keychain."""
        self.mock_keychain.get_password.return_value = "stored_key"

        key = self.writer.read_api_key()

        self.assertEqual(key, "stored_key")
        self.mock_keychain.get_password.assert_called_once_with(
            "com.mlfoundry.flow",
            "api_key"
        )

    def test_read_api_key_not_found(self):
        """Test reading API key when not in keychain."""
        self.mock_keychain.get_password.return_value = None

        key = self.writer.read_api_key()

        self.assertIsNone(key)

    def test_read_api_key_error(self):
        """Test reading API key when keychain fails."""
        self.mock_keychain.get_password.side_effect = Exception("Keychain error")

        key = self.writer.read_api_key()

        self.assertIsNone(key)

    def test_atomic_write(self):
        """Test atomic file writing."""
        # Write initial config
        config1 = {"project": "project1", "region": "us-central1-a"}
        validation = ValidationResult(is_valid=True, projects=[])
        self.writer.write(config1, validation)

        # Simulate write failure during second write
        config2 = {"project": "project2", "region": "eu-west1"}

        with patch('tempfile.NamedTemporaryFile') as mock_temp:
            # Make temp file creation fail after write
            mock_file = MagicMock()
            mock_temp.return_value.__enter__.return_value = mock_file
            mock_file.name = "/nonexistent/path"

            try:
                self.writer.write(config2, validation)
            except:
                pass

        # Original config should still be intact
        with open(self.config_path) as f:
            saved_config = yaml.safe_load(f)

        self.assertEqual(saved_config["project"], "project1")

    def test_creates_parent_directory(self):
        """Test that parent directory is created if missing."""
        nested_path = Path(self.temp_dir) / "nested" / "dir" / "config.yaml"
        writer = ConfigWriter(
            keychain=self.mock_keychain,
            config_path=nested_path
        )

        config = {"project": "test"}
        validation = ValidationResult(is_valid=True, projects=[])

        writer.write(config, validation)

        self.assertTrue(nested_path.exists())
        self.assertTrue(nested_path.parent.exists())
