"""Tests for OdooConnection write operations (create, write, unlink)."""

import os
from unittest.mock import Mock, patch

import pytest

from mcp_server_odoo.config import OdooConfig
from mcp_server_odoo.odoo_connection import OdooConnection, OdooConnectionError


class TestWriteOperations:
    """Test write operations in OdooConnection."""

    @pytest.fixture
    def mock_config(self):
        """Create mock config."""
        config = Mock(spec=OdooConfig)
        config.url = os.getenv("ODOO_URL", "http://localhost:8069")
        config.db = "test"
        config.username = "test"
        config.password = "test"
        config.api_key = None
        config.uses_api_key = False
        config.uses_credentials = True
        return config

    @pytest.fixture
    def mock_performance_manager(self, mock_config):
        """Create mock performance manager."""
        manager = Mock()
        # Create a proper context manager mock
        cm = Mock()
        cm.__enter__ = Mock(return_value=None)
        cm.__exit__ = Mock(return_value=None)
        manager.monitor.track_operation.return_value = cm
        manager.invalidate_record_cache = Mock()
        return manager

    @pytest.fixture
    def connection(self, mock_config, mock_performance_manager):
        """Create OdooConnection with mocks."""
        conn = OdooConnection(mock_config, performance_manager=mock_performance_manager)
        conn._connected = True
        conn._authenticated = True
        conn._uid = 2
        conn._database = "test"
        conn._auth_method = "password"
        return conn

    def test_create_record(self, connection):
        """Test creating a record."""
        model = "res.partner"
        values = {"name": "Test Partner", "email": "test@example.com"}
        expected_id = 123

        with patch.object(connection, "execute_kw", return_value=expected_id) as mock_execute:
            result = connection.create(model, values)

            assert result == expected_id
            mock_execute.assert_called_once_with(model, "create", [values], {})
            connection._performance_manager.invalidate_record_cache.assert_called_once_with(model)

    def test_create_record_error(self, connection):
        """Test create record with error."""
        model = "res.partner"
        values = {"name": "Test"}

        with patch.object(
            connection, "execute_kw", side_effect=OdooConnectionError("Create failed")
        ):
            with pytest.raises(OdooConnectionError, match="Create failed"):
                connection.create(model, values)

    def test_write_records(self, connection):
        """Test updating records."""
        model = "res.partner"
        ids = [123, 124]
        values = {"email": "updated@example.com"}

        with patch.object(connection, "execute_kw", return_value=True) as mock_execute:
            result = connection.write(model, ids, values)

            assert result is True
            mock_execute.assert_called_once_with(model, "write", [ids, values], {})
            # Should invalidate cache for each record
            assert connection._performance_manager.invalidate_record_cache.call_count == 2
            connection._performance_manager.invalidate_record_cache.assert_any_call(model, 123)
            connection._performance_manager.invalidate_record_cache.assert_any_call(model, 124)

    def test_write_single_record(self, connection):
        """Test updating a single record."""
        model = "res.partner"
        ids = [123]
        values = {"name": "Updated Name"}

        with patch.object(connection, "execute_kw", return_value=True):
            result = connection.write(model, ids, values)

            assert result is True
            connection._performance_manager.invalidate_record_cache.assert_called_once_with(
                model, 123
            )

    def test_write_records_error(self, connection):
        """Test write records with error."""
        model = "res.partner"
        ids = [123]
        values = {"name": "Test"}

        with patch.object(
            connection, "execute_kw", side_effect=OdooConnectionError("Write failed")
        ):
            with pytest.raises(OdooConnectionError, match="Write failed"):
                connection.write(model, ids, values)

    def test_unlink_records(self, connection):
        """Test deleting records."""
        model = "res.partner"
        ids = [123, 124, 125]

        with patch.object(connection, "execute_kw", return_value=True) as mock_execute:
            result = connection.unlink(model, ids)

            assert result is True
            mock_execute.assert_called_once_with(model, "unlink", [ids], {})
            # Should invalidate cache for each record
            assert connection._performance_manager.invalidate_record_cache.call_count == 3
            connection._performance_manager.invalidate_record_cache.assert_any_call(model, 123)
            connection._performance_manager.invalidate_record_cache.assert_any_call(model, 124)
            connection._performance_manager.invalidate_record_cache.assert_any_call(model, 125)

    def test_unlink_single_record(self, connection):
        """Test deleting a single record."""
        model = "res.partner"
        ids = [123]

        with patch.object(connection, "execute_kw", return_value=True):
            result = connection.unlink(model, ids)

            assert result is True
            connection._performance_manager.invalidate_record_cache.assert_called_once_with(
                model, 123
            )

    def test_unlink_records_error(self, connection):
        """Test unlink records with error."""
        model = "res.partner"
        ids = [123]

        with patch.object(
            connection, "execute_kw", side_effect=OdooConnectionError("Delete failed")
        ):
            with pytest.raises(OdooConnectionError, match="Delete failed"):
                connection.unlink(model, ids)

    def test_write_operations_performance_tracking(self, connection):
        """Test that write operations track performance."""
        # Test create
        with patch.object(connection, "execute_kw", return_value=123):
            connection.create("res.partner", {"name": "Test"})
            connection._performance_manager.monitor.track_operation.assert_called_with(
                "create_res.partner"
            )

        # Test write
        with patch.object(connection, "execute_kw", return_value=True):
            connection.write("res.partner", [123], {"name": "Updated"})
            connection._performance_manager.monitor.track_operation.assert_called_with(
                "write_res.partner"
            )

        # Test unlink
        with patch.object(connection, "execute_kw", return_value=True):
            connection.unlink("res.partner", [123])
            connection._performance_manager.monitor.track_operation.assert_called_with(
                "unlink_res.partner"
            )

    def test_write_operations_not_authenticated(self, connection):
        """Test write operations when not authenticated."""
        connection._authenticated = False

        # Since execute_kw checks authentication, the error is raised from there
        with pytest.raises(OdooConnectionError, match="Not authenticated"):
            connection.create("res.partner", {"name": "Test"})

        # Reset for next test
        connection._authenticated = False
        with pytest.raises(OdooConnectionError, match="Not authenticated"):
            connection.write("res.partner", [123], {"name": "Test"})

        # Reset for next test
        connection._authenticated = False
        with pytest.raises(OdooConnectionError, match="Not authenticated"):
            connection.unlink("res.partner", [123])
