"""Tests for Snowflake executor factory with session token support."""

import pytest
from unittest.mock import Mock, patch, MagicMock
from test_runner.validate.targets.snowflake import SnowflakeExecutorFactory
from test_runner.validate.targets.snowflake_connection_config import SessionTokenConfig
from test_runner.common.database_dialect import DatabaseDialect


class TestSnowflakeExecutorFactorySessionToken:
    """Test suite for SnowflakeExecutorFactory session token support."""

    def test_dialect_returns_snowflake(self):
        """Test that dialect property returns SNOWFLAKE."""
        factory = SnowflakeExecutorFactory()
        assert factory.dialect == DatabaseDialect.SNOWFLAKE

    def test_build_config_with_named_connection(self):
        """Test build_config with traditional named connection."""
        factory = SnowflakeExecutorFactory()
        raw = {"mode": "name", "name": "my_connection"}
        
        config = factory.build_config(raw)
        
        assert config.mode == "name"
        assert config.name == "my_connection"

    def test_build_config_with_session_token_valid(self):
        """Test build_config with valid session token configuration."""
        factory = SnowflakeExecutorFactory()
        raw = {
            "mode": "session_token",
            "session_token": "token_123",
            "master_token": "master_456",
            "account": "myaccount",
            "user": "myuser",
            "role": "SYSADMIN",
            "warehouse": "MY_WH"
        }
        
        config = factory.build_config(raw)
        
        # Should return SessionTokenConfig object
        assert isinstance(config, SessionTokenConfig)
        assert config.session_token == "token_123"
        assert config.master_token == "master_456"
        assert config.account == "myaccount"
        assert config.user == "myuser"

    def test_validate_session_token_missing_both_tokens(self):
        """Test validation fails when both tokens are missing."""
        factory = SnowflakeExecutorFactory()
        raw = {
            "mode": "session_token",
            "account": "myaccount",
            "user": "myuser"
        }
        
        with pytest.raises(ValueError, match="Session token mode requires both 'session_token' and 'master_token'"):
            factory.build_config(raw)

    def test_validate_session_token_missing_master_token(self):
        """Test validation fails when master token is missing."""
        factory = SnowflakeExecutorFactory()
        raw = {
            "mode": "session_token",
            "session_token": "token_123",
            "account": "myaccount",
            "user": "myuser"
        }
        
        with pytest.raises(ValueError, match="When using a session token, you must provide the corresponding master token"):
            factory.build_config(raw)

    def test_validate_session_token_missing_session_token(self):
        """Test validation fails when session token is missing."""
        factory = SnowflakeExecutorFactory()
        raw = {
            "mode": "session_token",
            "master_token": "master_456",
            "account": "myaccount",
            "user": "myuser"
        }
        
        with pytest.raises(ValueError, match="When using a master token, you must provide the corresponding session token"):
            factory.build_config(raw)

    def test_validate_session_token_missing_account(self):
        """Test validation fails when account is missing."""
        factory = SnowflakeExecutorFactory()
        raw = {
            "mode": "session_token",
            "session_token": "token_123",
            "master_token": "master_456",
            "user": "myuser"
        }
        
        with pytest.raises(ValueError, match="Session token mode requires 'account'"):
            factory.build_config(raw)

    def test_validate_session_token_missing_user(self):
        """Test validation fails when user is missing."""
        factory = SnowflakeExecutorFactory()
        raw = {
            "mode": "session_token",
            "session_token": "token_123",
            "master_token": "master_456",
            "account": "myaccount"
        }
        
        with pytest.raises(ValueError, match="Session token mode requires 'user'"):
            factory.build_config(raw)

    def test_validate_session_token_empty_account(self):
        """Test validation fails when account is empty string."""
        factory = SnowflakeExecutorFactory()
        raw = {
            "mode": "session_token",
            "session_token": "token_123",
            "master_token": "master_456",
            "account": "",
            "user": "myuser"
        }
        
        with pytest.raises(ValueError, match="Session token mode requires 'account'"):
            factory.build_config(raw)

    def test_validate_session_token_empty_user(self):
        """Test validation fails when user is empty string."""
        factory = SnowflakeExecutorFactory()
        raw = {
            "mode": "session_token",
            "session_token": "token_123",
            "master_token": "master_456",
            "account": "myaccount",
            "user": ""
        }
        
        with pytest.raises(ValueError, match="Session token mode requires 'user'"):
            factory.build_config(raw)

    @patch('snowflake.connector.connect')
    def test_create_executor_with_session_token_config(self, mock_connect):
        """Test create_executor with SessionTokenConfig object."""
        factory = SnowflakeExecutorFactory()
        mock_connection = Mock()
        mock_connect.return_value = mock_connection
        
        config = SessionTokenConfig(
            session_token="token_123",
            master_token="master_456",
            account="myaccount",
            user="myuser",
            role="SYSADMIN",
            warehouse="MY_WH",
            database="MY_DB",
            schema="MY_SCHEMA"
        )
        
        executor = factory.create_executor(config)
        
        # Verify snowflake.connector.connect was called with correct parameters
        mock_connect.assert_called_once_with(
            session_token="token_123",
            master_token="master_456",
            account="myaccount",
            user="myuser",
            role="SYSADMIN",
            warehouse="MY_WH",
            database="MY_DB",
            schema="MY_SCHEMA",
            server_session_keep_alive=True,
            application="SNOWCONVERT_TEST_RUNNER"
        )
        
        assert executor is not None

    @patch('snowflake.connector.connect')
    def test_create_executor_with_session_token_minimal(self, mock_connect):
        """Test create_executor with minimal session token configuration."""
        factory = SnowflakeExecutorFactory()
        mock_connection = Mock()
        mock_connect.return_value = mock_connection
        
        config = SessionTokenConfig(
            session_token="token_123",
            master_token="master_456",
            account="myaccount",
            user="myuser"
        )
        
        executor = factory.create_executor(config)
        
        # Verify connection called with required params only (optional ones omitted when None)
        mock_connect.assert_called_once()
        call_kwargs = mock_connect.call_args[1]
        assert call_kwargs["session_token"] == "token_123"
        assert call_kwargs["master_token"] == "master_456"
        assert call_kwargs["account"] == "myaccount"
        assert call_kwargs["user"] == "myuser"
        assert call_kwargs["server_session_keep_alive"] is True
        assert call_kwargs["application"] == "SNOWCONVERT_TEST_RUNNER"
        # Optional params should not be present when None
        assert "role" not in call_kwargs
        assert "warehouse" not in call_kwargs
        assert "database" not in call_kwargs
        assert "schema" not in call_kwargs

    @patch('test_runner.validate.targets.snowflake_connection_manager.create_connector_from_connection')
    def test_create_executor_from_connection(self, mock_create_connector):
        """Test create_executor_from_connection wraps existing connection."""
        factory = SnowflakeExecutorFactory()
        mock_connection = Mock()
        mock_connector = Mock()
        mock_create_connector.return_value = mock_connector
        
        executor = factory.create_executor_from_connection(
            mock_connection,
            owns_connection=True
        )
        
        mock_create_connector.assert_called_once_with(
            mock_connection,
            owns_connection=True
        )
        assert executor is not None

    @patch('test_runner.validate.targets.snowflake_connection_manager.create_connector_from_connection')
    def test_create_executor_from_connection_default_ownership(self, mock_create_connector):
        """Test create_executor_from_connection with default ownership."""
        factory = SnowflakeExecutorFactory()
        mock_connection = Mock()
        mock_connector = Mock()
        mock_create_connector.return_value = mock_connector
        
        executor = factory.create_executor_from_connection(mock_connection)
        
        mock_create_connector.assert_called_once_with(
            mock_connection,
            owns_connection=False
        )
