"""Unit tests for MutationOperations (Low-Level API)."""

from __future__ import annotations

from uuid import UUID

import pytest

from questra_automation.models import ExecutionInitiator, RepositoryAuthenticationMethod
from questra_automation.operations import MutationOperations


@pytest.mark.unit
class TestMutationOperationsExecuteAutomation:
    """Tests für execute_automation() Methode."""

    def test_execute_automation_success(
        self, mock_graphql_execute, execute_success_response
    ):
        """Test execute_automation - erfolgreich."""
        mock_graphql_execute.return_value = execute_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.execute_automation(
            workspace_name="dev-workspace",
            automation_path="scripts/daily_sync.py",
            initiator_type=ExecutionInitiator.MANUAL,
            arguments=[
                {"name": "source", "value": "system-a"},
                {"name": "target", "value": "system-b"},
            ],
            initiator_reference="user@example.com",
        )

        # Response validieren
        assert isinstance(result.id, UUID)
        assert str(result.id) == "12345678-1234-1234-1234-123456789012"

        # GraphQL Execute wurde aufgerufen
        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args

        # Mutation Query validieren
        mutation = call_args[0][0]
        assert "mutation" in mutation
        assert "execute" in mutation

        # Variables prüfen
        variables = call_args[0][1]
        assert variables["input"]["workspaceName"] == "dev-workspace"
        assert variables["input"]["automationPath"] == "scripts/daily_sync.py"
        assert variables["input"]["initiatorType"] == "MANUAL"
        assert len(variables["input"]["arguments"]) == 2
        assert variables["input"]["initiatorReference"] == "user@example.com"

    def test_execute_automation_minimal(
        self, mock_graphql_execute, execute_success_response
    ):
        """Test execute_automation mit minimalen Parametern."""
        mock_graphql_execute.return_value = execute_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.execute_automation(
            workspace_name="dev-workspace",
            automation_path="scripts/cleanup.py",
            initiator_type=ExecutionInitiator.SCRIPT,
        )

        assert isinstance(result.id, UUID)

        # Variables validieren
        call_args = mock_graphql_execute.call_args
        variables = call_args[0][1]
        assert variables["input"]["arguments"] == []
        assert variables["input"]["initiatorReference"] is None


@pytest.mark.unit
class TestMutationOperationsCreateRepository:
    """Tests für create_repository() Methode."""

    def test_create_repository_success(
        self, mock_graphql_execute, create_repository_success_response
    ):
        """Test create_repository - erfolgreich erstellt."""
        mock_graphql_execute.return_value = create_repository_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.create_repository(
            name="new-repo",
            url="https://github.com/example/new-repo.git",
            credentials_method=RepositoryAuthenticationMethod.USERNAME_PASSWORD,
            username="deploy-user",
            password="secret-token",
        )

        # Response validieren
        assert result.name == "new-repo"

        # GraphQL Execute wurde aufgerufen
        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args

        # Mutation Query validieren
        mutation = call_args[0][0]
        assert "createRepository" in mutation

        # Input-Daten prüfen
        variables = call_args[0][1]
        assert variables["input"]["name"] == "new-repo"
        assert variables["input"]["remote"]["url"] == "https://github.com/example/new-repo.git"
        assert (
            variables["input"]["remote"]["credentials"]["method"] == "USERNAME_PASSWORD"
        )
        assert variables["input"]["remote"]["credentials"]["username"] == "deploy-user"
        assert variables["input"]["remote"]["credentials"]["password"] == "secret-token"

    def test_create_repository_ssh_key(
        self, mock_graphql_execute, create_repository_success_response
    ):
        """Test create_repository mit SSH Key."""
        mock_graphql_execute.return_value = create_repository_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.create_repository(
            name="new-repo",
            url="git@github.com:example/new-repo.git",
            credentials_method=RepositoryAuthenticationMethod.SSH_KEY,
        )

        assert result.name == "new-repo"

        # Variables prüfen
        call_args = mock_graphql_execute.call_args
        variables = call_args[0][1]
        assert variables["input"]["remote"]["credentials"]["method"] == "SSH_KEY"
        assert variables["input"]["remote"]["credentials"]["username"] is None
        assert variables["input"]["remote"]["credentials"]["password"] is None


@pytest.mark.unit
class TestMutationOperationsUpdateRepository:
    """Tests für update_repository() Methode."""

    def test_update_repository_success(
        self, mock_graphql_execute, update_repository_success_response
    ):
        """Test update_repository - erfolgreich aktualisiert."""
        mock_graphql_execute.return_value = update_repository_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.update_repository(
            name="old-repo",
            new_name="updated-repo",
            url="https://github.com/example/updated-repo.git",
            credentials_method=RepositoryAuthenticationMethod.USERNAME_PASSWORD,
            username="new-user",
            password="new-password",
        )

        assert result.name == "updated-repo"

        call_args = mock_graphql_execute.call_args
        variables = call_args[0][1]
        assert variables["input"]["name"] == "old-repo"
        assert variables["input"]["newName"] == "updated-repo"
        assert variables["input"]["remote"]["url"] == "https://github.com/example/updated-repo.git"

    def test_update_repository_name_only(
        self, mock_graphql_execute, update_repository_success_response
    ):
        """Test update_repository - nur Name ändern."""
        mock_graphql_execute.return_value = update_repository_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.update_repository(
            name="old-repo",
            new_name="new-repo",
        )

        assert result.name == "updated-repo"

        call_args = mock_graphql_execute.call_args
        variables = call_args[0][1]
        assert variables["input"]["newName"] == "new-repo"
        assert variables["input"]["remote"] is None


@pytest.mark.unit
class TestMutationOperationsDeleteRepository:
    """Tests für delete_repository() Methode."""

    def test_delete_repository_success(
        self, mock_graphql_execute, delete_repository_success_response
    ):
        """Test delete_repository - erfolgreich gelöscht."""
        mock_graphql_execute.return_value = delete_repository_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.delete_repository(name="deleted-repo")

        assert result.name == "deleted-repo"

        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args
        mutation = call_args[0][0]
        assert "deleteRepository" in mutation

        variables = call_args[0][1]
        assert variables["input"]["name"] == "deleted-repo"


@pytest.mark.unit
class TestMutationOperationsRenewRepositorySshKey:
    """Tests für renew_repository_ssh_key() Methode."""

    def test_renew_ssh_key_success(
        self, mock_graphql_execute, renew_ssh_key_success_response
    ):
        """Test renew_repository_ssh_key - erfolgreich erneuert."""
        mock_graphql_execute.return_value = renew_ssh_key_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.renew_repository_ssh_key(name="test-repo")

        assert result.name == "test-repo"

        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args
        assert "renewRepositorySshKey" in call_args[0][0]


@pytest.mark.unit
class TestMutationOperationsCreateWorkspace:
    """Tests für create_workspace() Methode."""

    def test_create_workspace_success(
        self, mock_graphql_execute, create_workspace_success_response
    ):
        """Test create_workspace - erfolgreich erstellt."""
        mock_graphql_execute.return_value = create_workspace_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.create_workspace(
            repository_name="main-repo",
            name="new-workspace",
            branch_name="main",
            commit="abc123def456",
        )

        assert result.name == "new-workspace"

        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args

        mutation = call_args[0][0]
        assert "createWorkspace" in mutation

        variables = call_args[0][1]
        assert variables["input"]["repositoryName"] == "main-repo"
        assert variables["input"]["name"] == "new-workspace"
        assert variables["input"]["branch"]["name"] == "main"
        assert variables["input"]["branch"]["commit"] == "abc123def456"


@pytest.mark.unit
class TestMutationOperationsUpdateWorkspace:
    """Tests für update_workspace() Methode."""

    def test_update_workspace_success(
        self, mock_graphql_execute, update_workspace_success_response
    ):
        """Test update_workspace - erfolgreich aktualisiert."""
        mock_graphql_execute.return_value = update_workspace_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.update_workspace(
            name="old-workspace",
            new_name="updated-workspace",
            branch_name="develop",
            commit="def456ghi789",
        )

        assert result.name == "updated-workspace"

        call_args = mock_graphql_execute.call_args
        variables = call_args[0][1]
        assert variables["input"]["name"] == "old-workspace"
        assert variables["input"]["newName"] == "updated-workspace"
        assert variables["input"]["branch"]["name"] == "develop"
        assert variables["input"]["branch"]["commit"] == "def456ghi789"

    def test_update_workspace_name_only(
        self, mock_graphql_execute, update_workspace_success_response
    ):
        """Test update_workspace - nur Name ändern."""
        mock_graphql_execute.return_value = update_workspace_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.update_workspace(
            name="old-workspace",
            new_name="new-workspace",
        )

        assert result.name == "updated-workspace"

        call_args = mock_graphql_execute.call_args
        variables = call_args[0][1]
        assert variables["input"]["branch"] is None


@pytest.mark.unit
class TestMutationOperationsDeleteWorkspace:
    """Tests für delete_workspace() Methode."""

    def test_delete_workspace_success(
        self, mock_graphql_execute, delete_workspace_success_response
    ):
        """Test delete_workspace - erfolgreich gelöscht."""
        mock_graphql_execute.return_value = delete_workspace_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.delete_workspace(name="deleted-workspace")

        assert result.name == "deleted-workspace"

        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args
        assert "deleteWorkspace" in call_args[0][0]


@pytest.mark.unit
class TestMutationOperationsSynchronizeWorkspace:
    """Tests für synchronize_workspace() Methode."""

    def test_synchronize_workspace_success(
        self, mock_graphql_execute, synchronize_workspace_success_response
    ):
        """Test synchronize_workspace - erfolgreich synchronisiert."""
        mock_graphql_execute.return_value = synchronize_workspace_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.synchronize_workspace(name="synced-workspace")

        assert result.name == "synced-workspace"

        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args
        assert "synchronizeWorkspace" in call_args[0][0]


@pytest.mark.unit
class TestMutationOperationsCreateSchedule:
    """Tests für create_schedule() Methode."""

    def test_create_schedule_success(
        self, mock_graphql_execute, create_schedule_success_response
    ):
        """Test create_schedule - erfolgreich erstellt."""
        mock_graphql_execute.return_value = create_schedule_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.create_schedule(
            workspace_name="dev-workspace",
            automation_path="scripts/daily_sync.py",
            name="new-schedule",
            description="Runs daily at 2 AM",
            cron="0 2 * * *",
            timezone="Europe/Berlin",
            active=True,
            arguments=[
                {"name": "source", "value": "system-a"},
                {"name": "target", "value": "system-b"},
            ],
        )

        # Response validieren
        assert result.name == "new-schedule"
        assert result.workspace_name == "dev-workspace"
        assert result.automation_path == "scripts/daily_sync.py"

        # GraphQL Execute wurde aufgerufen
        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args

        mutation = call_args[0][0]
        assert "createSchedule" in mutation

        variables = call_args[0][1]
        assert variables["input"]["workspaceName"] == "dev-workspace"
        assert variables["input"]["automationPath"] == "scripts/daily_sync.py"
        assert variables["input"]["name"] == "new-schedule"
        assert variables["input"]["description"] == "Runs daily at 2 AM"
        assert variables["input"]["cron"] == "0 2 * * *"
        assert variables["input"]["timezone"] == "Europe/Berlin"
        assert variables["input"]["active"] is True
        assert len(variables["input"]["arguments"]) == 2

    def test_create_schedule_minimal(
        self, mock_graphql_execute, create_schedule_success_response
    ):
        """Test create_schedule mit minimalen Parametern."""
        mock_graphql_execute.return_value = create_schedule_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.create_schedule(
            workspace_name="dev-workspace",
            automation_path="scripts/cleanup.py",
            name="minimal-schedule",
            description="Minimal schedule",
            cron="0 0 * * 0",
            timezone="UTC",
            active=False,
        )

        assert result.name == "new-schedule"

        call_args = mock_graphql_execute.call_args
        variables = call_args[0][1]
        assert variables["input"]["arguments"] == []


@pytest.mark.unit
class TestMutationOperationsUpdateSchedule:
    """Tests für update_schedule() Methode."""

    def test_update_schedule_success(
        self, mock_graphql_execute, update_schedule_success_response
    ):
        """Test update_schedule - erfolgreich aktualisiert."""
        mock_graphql_execute.return_value = update_schedule_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.update_schedule(
            workspace_name="dev-workspace",
            automation_path="scripts/daily_sync.py",
            name="old-schedule",
            new_name="updated-schedule",
            description="Updated description",
            cron="0 3 * * *",
            timezone="UTC",
            active=False,
            arguments=[{"name": "mode", "value": "test"}],
        )

        assert result.name == "updated-schedule"
        assert result.workspace_name == "dev-workspace"
        assert result.automation_path == "scripts/daily_sync.py"

        call_args = mock_graphql_execute.call_args
        variables = call_args[0][1]
        assert variables["input"]["name"] == "old-schedule"
        assert variables["input"]["newName"] == "updated-schedule"
        assert variables["input"]["cron"] == "0 3 * * *"


@pytest.mark.unit
class TestMutationOperationsDeleteSchedule:
    """Tests für delete_schedule() Methode."""

    def test_delete_schedule_success(
        self, mock_graphql_execute, delete_schedule_success_response
    ):
        """Test delete_schedule - erfolgreich gelöscht."""
        mock_graphql_execute.return_value = delete_schedule_success_response

        mutations = MutationOperations(mock_graphql_execute)
        result = mutations.delete_schedule(
            workspace_name="dev-workspace",
            automation_path="scripts/daily_sync.py",
            name="deleted-schedule",
        )

        assert result.name == "deleted-schedule"
        assert result.workspace_name == "dev-workspace"
        assert result.automation_path == "scripts/daily_sync.py"

        mock_graphql_execute.assert_called_once()
        call_args = mock_graphql_execute.call_args
        assert "deleteSchedule" in call_args[0][0]

        variables = call_args[0][1]
        assert variables["input"]["workspaceName"] == "dev-workspace"
        assert variables["input"]["automationPath"] == "scripts/daily_sync.py"
        assert variables["input"]["name"] == "deleted-schedule"
