"""
Unit tests for scheduling/schedule/safe_scripting.py

Tests safe scripting engine for customer automations.
"""
from unittest.mock import Mock, patch, MagicMock
import pytest


class TestResourceLimitExceededException:
    """Tests for ResourceLimitExceeded exception."""

    def test_exception_exists(self):
        """Should have ResourceLimitExceeded exception class."""
        from smoothschedule.scheduling.schedule.safe_scripting import ResourceLimitExceeded

        assert issubclass(ResourceLimitExceeded, Exception)

    def test_can_raise_with_message(self):
        """Should be raisable with a message."""
        from smoothschedule.scheduling.schedule.safe_scripting import ResourceLimitExceeded

        with pytest.raises(ResourceLimitExceeded) as exc_info:
            raise ResourceLimitExceeded("Limit exceeded")

        assert str(exc_info.value) == "Limit exceeded"


class TestScriptExecutionErrorException:
    """Tests for ScriptExecutionError exception."""

    def test_exception_exists(self):
        """Should have ScriptExecutionError exception class."""
        from smoothschedule.scheduling.schedule.safe_scripting import ScriptExecutionError

        assert issubclass(ScriptExecutionError, Exception)

    def test_can_raise_with_message(self):
        """Should be raisable with a message."""
        from smoothschedule.scheduling.schedule.safe_scripting import ScriptExecutionError

        with pytest.raises(ScriptExecutionError) as exc_info:
            raise ScriptExecutionError("Script failed")

        assert str(exc_info.value) == "Script failed"


class TestSafeScriptAPIClass:
    """Tests for SafeScriptAPI class."""

    def test_class_exists(self):
        """Should have SafeScriptAPI class."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        assert SafeScriptAPI is not None

    def test_init_stores_business(self):
        """Should store business on initialization."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_business = Mock()
        mock_user = Mock()
        mock_context = {}

        api = SafeScriptAPI(business=mock_business, user=mock_user, execution_context=mock_context)

        assert api.business is mock_business

    def test_init_stores_user(self):
        """Should store user on initialization."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_business = Mock()
        mock_user = Mock()
        mock_context = {}

        api = SafeScriptAPI(business=mock_business, user=mock_user, execution_context=mock_context)

        assert api.user is mock_user

    def test_init_stores_context(self):
        """Should store context on initialization."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_business = Mock()
        mock_user = Mock()
        mock_context = {'key': 'value'}

        api = SafeScriptAPI(business=mock_business, user=mock_user, execution_context=mock_context)

        assert api.context == mock_context

    def test_init_sets_api_call_count_to_zero(self):
        """Should initialize API call count to zero."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        assert api._api_call_count == 0

    def test_has_max_api_calls_limit(self):
        """Should have max API calls limit."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        assert api._max_api_calls > 0


class TestSafeScriptAPICheckLimit:
    """Tests for _check_api_limit method."""

    def test_increments_call_count(self):
        """Should increment API call count."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        initial_count = api._api_call_count

        api._check_api_limit()

        assert api._api_call_count == initial_count + 1

    def test_raises_when_limit_exceeded(self):
        """Should raise ResourceLimitExceeded when limit reached."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ResourceLimitExceeded

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api._api_call_count = api._max_api_calls  # Set to max

        with pytest.raises(ResourceLimitExceeded) as exc_info:
            api._check_api_limit()

        assert "API call limit exceeded" in str(exc_info.value)


class TestSafeScriptAPIMethods:
    """Tests for SafeScriptAPI methods."""

    def test_has_get_appointments_method(self):
        """Should have get_appointments method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_appointments')
        assert callable(api.get_appointments)


class TestSafeScriptEngineClass:
    """Tests for SafeScriptEngine class."""

    def test_class_exists(self):
        """Should have SafeScriptEngine class."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        assert SafeScriptEngine is not None

    def test_has_validate_script_method(self):
        """Should have _validate_script method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()
        assert hasattr(engine, '_validate_script')
        assert callable(engine._validate_script)

    def test_has_execute_method(self):
        """Should have execute method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()
        assert hasattr(engine, 'execute')
        assert callable(engine.execute)


class TestSafeScriptEngineConstants:
    """Tests for SafeScriptEngine class constants."""

    def test_has_max_execution_time(self):
        """Should have MAX_EXECUTION_TIME constant."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        assert hasattr(SafeScriptEngine, 'MAX_EXECUTION_TIME')
        assert isinstance(SafeScriptEngine.MAX_EXECUTION_TIME, (int, float))

    def test_has_max_iterations(self):
        """Should have MAX_ITERATIONS constant."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        assert hasattr(SafeScriptEngine, 'MAX_ITERATIONS')
        assert isinstance(SafeScriptEngine.MAX_ITERATIONS, int)

    def test_has_max_output_size(self):
        """Should have MAX_OUTPUT_SIZE constant."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        assert hasattr(SafeScriptEngine, 'MAX_OUTPUT_SIZE')
        assert isinstance(SafeScriptEngine.MAX_OUTPUT_SIZE, int)

    def test_has_max_memory_mb(self):
        """Should have MAX_MEMORY_MB constant."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        assert hasattr(SafeScriptEngine, 'MAX_MEMORY_MB')
        assert isinstance(SafeScriptEngine.MAX_MEMORY_MB, int)

    def test_has_safe_builtins(self):
        """Should have SAFE_BUILTINS dict."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        assert hasattr(SafeScriptEngine, 'SAFE_BUILTINS')
        assert isinstance(SafeScriptEngine.SAFE_BUILTINS, dict)

    def test_safe_builtins_has_common_functions(self):
        """Should have common safe functions in builtins."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        assert 'len' in SafeScriptEngine.SAFE_BUILTINS
        assert 'range' in SafeScriptEngine.SAFE_BUILTINS
        assert 'min' in SafeScriptEngine.SAFE_BUILTINS
        assert 'max' in SafeScriptEngine.SAFE_BUILTINS


class TestSafeScriptEngineInit:
    """Tests for SafeScriptEngine initialization."""

    def test_init_sets_iteration_count_to_zero(self):
        """Should initialize iteration count to zero."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()

        assert engine._iteration_count == 0


class TestSafeScriptEngineCheckIterations:
    """Tests for _check_iterations method."""

    def test_increments_iteration_count(self):
        """Should increment iteration count."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()
        initial = engine._iteration_count

        engine._check_iterations()

        assert engine._iteration_count == initial + 1

    def test_raises_when_limit_exceeded(self):
        """Should raise ResourceLimitExceeded when limit reached."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ResourceLimitExceeded

        engine = SafeScriptEngine()
        engine._iteration_count = SafeScriptEngine.MAX_ITERATIONS

        with pytest.raises(ResourceLimitExceeded) as exc_info:
            engine._check_iterations()

        assert "iteration limit exceeded" in str(exc_info.value)


class TestAnalyzePluginHttpCalls:
    """Tests for analyze_plugin_http_calls function."""

    def test_function_exists(self):
        """Should have analyze_plugin_http_calls function."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        assert callable(analyze_plugin_http_calls)


class TestValidatePluginWhitelist:
    """Tests for validate_plugin_whitelist function."""

    def test_function_exists(self):
        """Should have validate_plugin_whitelist function."""
        from smoothschedule.scheduling.schedule.safe_scripting import validate_plugin_whitelist

        assert callable(validate_plugin_whitelist)

    def test_returns_dict(self):
        """Should return a dictionary."""
        from smoothschedule.scheduling.schedule.safe_scripting import validate_plugin_whitelist

        result = validate_plugin_whitelist("x = 1")

        assert isinstance(result, dict)

    def test_returns_valid_for_no_http_calls(self):
        """Should return valid when no HTTP calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import validate_plugin_whitelist

        result = validate_plugin_whitelist("x = 1\ny = 2")

        assert result['valid'] is True
        assert result['errors'] == []
        assert result['http_calls'] == []


class TestSafeScriptAPIGetCustomers:
    """Tests for SafeScriptAPI.get_customers method."""

    def test_has_get_customers_method(self):
        """Should have get_customers method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_customers')
        assert callable(api.get_customers)


class TestSafeScriptAPISendEmail:
    """Tests for SafeScriptAPI.send_email method."""

    def test_has_send_email_method(self):
        """Should have send_email method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'send_email')
        assert callable(api.send_email)

    def test_send_email_validates_address(self):
        """Should validate email address."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.send_email(to='invalid', subject='Test', body='Body')

        assert 'Invalid email' in str(exc_info.value)

    def test_send_email_subject_length_limit(self):
        """Should enforce subject length limit."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.send_email(to='test@example.com', subject='x' * 250, body='Body')

        assert 'Subject too long' in str(exc_info.value)

    def test_send_email_body_length_limit(self):
        """Should enforce body length limit."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.send_email(to='test@example.com', subject='Test', body='x' * 15000)

        assert 'Body too long' in str(exc_info.value)


class TestSafeScriptAPILog:
    """Tests for SafeScriptAPI.log method."""

    def test_has_log_method(self):
        """Should have log method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'log')
        assert callable(api.log)

    @patch('smoothschedule.scheduling.schedule.safe_scripting.logger')
    def test_log_writes_to_logger(self, mock_logger):
        """Should write to logger."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.log('Test message')

        mock_logger.info.assert_called_once()
        assert 'Test message' in str(mock_logger.info.call_args)


class TestSafeScriptAPIGetInsertionContext:
    """Tests for SafeScriptAPI._get_insertion_context method."""

    def test_returns_dict(self):
        """Should return a dictionary."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_business = Mock()
        mock_business.name = 'Test Business'
        mock_business.contact_email = 'test@example.com'
        mock_business.phone = '555-1234'

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})
        context = api._get_insertion_context()

        assert isinstance(context, dict)
        assert 'business_name' in context
        assert 'business_email' in context
        assert 'today' in context
        assert 'now' in context

    def test_handles_none_business(self):
        """Should handle None business gracefully."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=None, user=Mock(), execution_context={})
        context = api._get_insertion_context()

        assert context['business_name'] == ''
        assert context['business_email'] == ''


class TestSafeScriptAPIValidateUrl:
    """Tests for SafeScriptAPI._validate_url method."""

    def test_blocks_localhost(self):
        """Should block localhost URLs."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api._validate_url('http://localhost:8000/api')

        assert 'Cannot access localhost' in str(exc_info.value)

    def test_blocks_127_0_0_1(self):
        """Should block 127.0.0.1 URLs."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api._validate_url('http://127.0.0.1:8000/api')

        assert 'Cannot access localhost' in str(exc_info.value)


class TestSafeScriptAPIHttpMethods:
    """Tests for SafeScriptAPI HTTP methods."""

    def test_has_http_get_method(self):
        """Should have http_get method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'http_get')
        assert callable(api.http_get)

    def test_has_http_post_method(self):
        """Should have http_post method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'http_post')
        assert callable(api.http_post)

    def test_has_http_put_method(self):
        """Should have http_put method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'http_put')
        assert callable(api.http_put)

    def test_has_http_patch_method(self):
        """Should have http_patch method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'http_patch')
        assert callable(api.http_patch)

    def test_has_http_delete_method(self):
        """Should have http_delete method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'http_delete')
        assert callable(api.http_delete)


class TestSafeScriptAPICreateAppointment:
    """Tests for SafeScriptAPI.create_appointment method."""

    def test_has_create_appointment_method(self):
        """Should have create_appointment method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'create_appointment')
        assert callable(api.create_appointment)


class TestSafeScriptAPIUtilityMethods:
    """Tests for SafeScriptAPI utility methods."""

    def test_count_returns_length(self):
        """Should return length of list."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        result = api.count([1, 2, 3, 4, 5])

        assert result == 5

    def test_sum_returns_total(self):
        """Should return sum of items."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        result = api.sum([1, 2, 3, 4, 5])

        assert result == 15

    def test_filter_filters_items(self):
        """Should filter items by condition."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        items = [1, 2, 3, 4, 5]
        result = api.filter(items, lambda x: x > 2)

        assert result == [3, 4, 5]


class TestSafeScriptEngineValidateScript:
    """Tests for SafeScriptEngine._validate_script method."""

    def test_rejects_import_statements(self):
        """Should reject import statements."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ScriptExecutionError

        engine = SafeScriptEngine()

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine._validate_script('import os')

        assert 'Import statements not allowed' in str(exc_info.value)

    def test_rejects_from_imports(self):
        """Should reject from ... import statements."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ScriptExecutionError

        engine = SafeScriptEngine()

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine._validate_script('from os import path')

        assert 'Import statements not allowed' in str(exc_info.value)

    def test_rejects_exec_calls(self):
        """Should reject exec() calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ScriptExecutionError

        engine = SafeScriptEngine()

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine._validate_script('exec("print(1)")')

        assert "Function 'exec' not allowed" in str(exc_info.value)

    def test_rejects_eval_calls(self):
        """Should reject eval() calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ScriptExecutionError

        engine = SafeScriptEngine()

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine._validate_script('eval("1+1")')

        assert "Function 'eval' not allowed" in str(exc_info.value)

    def test_rejects_class_definitions(self):
        """Should reject class definitions."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ScriptExecutionError

        engine = SafeScriptEngine()

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine._validate_script('class Foo: pass')

        assert 'Class definitions not allowed' in str(exc_info.value)

    def test_rejects_function_definitions(self):
        """Should reject function definitions."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ScriptExecutionError

        engine = SafeScriptEngine()

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine._validate_script('def foo(): pass')

        assert 'Function definitions not allowed' in str(exc_info.value)

    def test_rejects_large_scripts(self):
        """Should reject scripts exceeding size limit."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ScriptExecutionError

        engine = SafeScriptEngine()
        large_script = 'x = 1\n' * 10000  # Create a large script

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine._validate_script(large_script)

        assert 'Script too large' in str(exc_info.value)

    def test_rejects_syntax_errors(self):
        """Should reject scripts with syntax errors."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, ScriptExecutionError

        engine = SafeScriptEngine()

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine._validate_script('if True\nprint("test")')

        assert 'Syntax error' in str(exc_info.value)

    def test_accepts_valid_script(self):
        """Should accept valid script."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()
        # Should not raise
        engine._validate_script('x = 1\ny = x + 2\nz = y * 3')


class TestSafeScriptEngineExecute:
    """Tests for SafeScriptEngine.execute method."""

    def test_executes_simple_script(self):
        """Should execute simple script."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, SafeScriptAPI

        engine = SafeScriptEngine()
        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        result = engine.execute('x = 1 + 2\nresult = x', api)

        assert result['success'] is True
        assert result['result'] == 3
        assert result['error'] is None

    def test_captures_print_output(self):
        """Should capture print output."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, SafeScriptAPI

        engine = SafeScriptEngine()
        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        # Note: print is in SAFE_BUILTINS
        result = engine.execute('x = 42', api)

        assert result['success'] is True

    def test_raises_for_invalid_script(self):
        """Should raise ScriptExecutionError for invalid script."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, SafeScriptAPI, ScriptExecutionError

        engine = SafeScriptEngine()
        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            engine.execute('import os', api)

        assert 'Import statements not allowed' in str(exc_info.value)

    def test_uses_initial_vars(self):
        """Should use initial variables."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine, SafeScriptAPI

        engine = SafeScriptEngine()
        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        result = engine.execute('result = my_var * 2', api, initial_vars={'my_var': 21})

        assert result['success'] is True
        assert result['result'] == 42


class TestSafeScriptEngineInjectLoopGuards:
    """Tests for SafeScriptEngine._inject_loop_guards method."""

    def test_has_inject_loop_guards_method(self):
        """Should have _inject_loop_guards method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()
        assert hasattr(engine, '_inject_loop_guards')
        assert callable(engine._inject_loop_guards)

    def test_handles_for_loops(self):
        """Should inject guards into for loops."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()
        script = 'for i in range(10):\n    x = i'
        result = engine._inject_loop_guards(script)

        assert '_iteration_check()' in result

    def test_handles_while_loops(self):
        """Should inject guards into while loops."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()
        script = 'x = 0\nwhile x < 10:\n    x += 1'
        result = engine._inject_loop_guards(script)

        assert '_iteration_check()' in result

    def test_handles_syntax_error(self):
        """Should return original script on syntax error."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptEngine

        engine = SafeScriptEngine()
        bad_script = 'for i in range('  # Invalid syntax
        result = engine._inject_loop_guards(bad_script)

        # Returns original script for invalid syntax
        assert result == bad_script


class TestAnalyzePluginHttpCallsDetection:
    """Tests for analyze_plugin_http_calls function detection."""

    def test_detects_http_get(self):
        """Should detect http_get calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        script = 'data = api.http_get("https://api.example.com/data")'
        calls = analyze_plugin_http_calls(script)

        assert len(calls) == 1
        assert calls[0]['method'] == 'GET'
        assert calls[0]['url'] == 'https://api.example.com/data'

    def test_detects_http_post(self):
        """Should detect http_post calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        script = 'api.http_post("https://api.example.com/data", {"key": "value"})'
        calls = analyze_plugin_http_calls(script)

        assert len(calls) == 1
        assert calls[0]['method'] == 'POST'

    def test_detects_http_put(self):
        """Should detect http_put calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        script = 'api.http_put("https://api.example.com/data", {"key": "value"})'
        calls = analyze_plugin_http_calls(script)

        assert len(calls) == 1
        assert calls[0]['method'] == 'PUT'

    def test_detects_http_patch(self):
        """Should detect http_patch calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        script = 'api.http_patch("https://api.example.com/data", {"key": "value"})'
        calls = analyze_plugin_http_calls(script)

        assert len(calls) == 1
        assert calls[0]['method'] == 'PATCH'

    def test_detects_http_delete(self):
        """Should detect http_delete calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        script = 'api.http_delete("https://api.example.com/data/123")'
        calls = analyze_plugin_http_calls(script)

        assert len(calls) == 1
        assert calls[0]['method'] == 'DELETE'

    def test_detects_multiple_calls(self):
        """Should detect multiple HTTP calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        script = '''
data = api.http_get("https://api.example.com/data")
api.http_post("https://api.example.com/other", data)
'''
        calls = analyze_plugin_http_calls(script)

        assert len(calls) == 2
        assert calls[0]['method'] == 'GET'
        assert calls[1]['method'] == 'POST'

    def test_handles_variable_urls(self):
        """Should handle variable URLs."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        script = 'data = api.http_get(url_var)'
        calls = analyze_plugin_http_calls(script)

        assert len(calls) == 1
        assert '<variable: url_var>' in calls[0]['url']

    def test_raises_on_syntax_error(self):
        """Should raise SyntaxError for invalid code."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        with pytest.raises(SyntaxError):
            analyze_plugin_http_calls('for i in range(')

    def test_returns_empty_for_no_http_calls(self):
        """Should return empty list when no HTTP calls."""
        from smoothschedule.scheduling.schedule.safe_scripting import analyze_plugin_http_calls

        script = 'x = 1\ny = 2'
        calls = analyze_plugin_http_calls(script)

        assert calls == []


class TestValidatePluginWhitelistValidation:
    """Tests for validate_plugin_whitelist validation logic."""

    def test_returns_valid_structure(self):
        """Should return expected structure."""
        from smoothschedule.scheduling.schedule.safe_scripting import validate_plugin_whitelist

        result = validate_plugin_whitelist("x = 1")

        assert 'valid' in result
        assert 'errors' in result
        assert 'warnings' in result
        assert 'http_calls' in result

    def test_invalid_on_syntax_error(self):
        """Should return invalid on syntax error."""
        from smoothschedule.scheduling.schedule.safe_scripting import validate_plugin_whitelist

        result = validate_plugin_whitelist("for i in range(")

        assert result['valid'] is False
        assert any('Syntax error' in err for err in result['errors'])

    def test_warnings_for_dynamic_urls(self):
        """Should add warnings for dynamic URLs."""
        from smoothschedule.scheduling.schedule.safe_scripting import validate_plugin_whitelist

        script = 'data = api.http_get(url_variable)'
        result = validate_plugin_whitelist(script)

        assert len(result['warnings']) > 0
        assert any('dynamic URL' in w for w in result['warnings'])


# =========================================================================
# TESTS FOR NEW API METHODS
# =========================================================================


class TestSafeScriptAPIFeatureCheck:
    """Tests for SafeScriptAPI._check_feature method."""

    def test_has_check_feature_method(self):
        """Should have _check_feature method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, '_check_feature')
        assert callable(api._check_feature)

    def test_raises_when_feature_not_available(self):
        """Should raise ScriptExecutionError when feature not available."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=False)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api._check_feature('sms_enabled', 'SMS messaging')

        assert 'not available on your plan' in str(exc_info.value)

    def test_passes_when_feature_available(self):
        """Should not raise when feature is available."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=True)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        # Should not raise
        api._check_feature('sms_enabled', 'SMS messaging')
        mock_business.has_feature.assert_called_with('sms_enabled')


class TestSafeScriptAPISendSMS:
    """Tests for SafeScriptAPI.send_sms method."""

    def test_has_send_sms_method(self):
        """Should have send_sms method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'send_sms')
        assert callable(api.send_sms)

    def test_validates_phone_number(self):
        """Should validate phone number."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=True)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.send_sms('123', 'Test message')

        assert 'Invalid phone number' in str(exc_info.value)

    def test_validates_message_length(self):
        """Should validate message length."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=True)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.send_sms('+15551234567', 'x' * 2000)

        assert 'too long' in str(exc_info.value)


class TestSafeScriptAPIGetSMSBalance:
    """Tests for SafeScriptAPI.get_sms_balance method."""

    def test_has_get_sms_balance_method(self):
        """Should have get_sms_balance method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_sms_balance')
        assert callable(api.get_sms_balance)


class TestSafeScriptAPIGetResources:
    """Tests for SafeScriptAPI.get_resources method."""

    def test_has_get_resources_method(self):
        """Should have get_resources method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_resources')
        assert callable(api.get_resources)

    @patch('smoothschedule.scheduling.schedule.models.Resource')
    def test_returns_list(self, mock_resource_class):
        """Should return a list of resources."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_resource = Mock()
        mock_resource.id = 1
        mock_resource.name = 'Test Resource'
        mock_resource.type = 'STAFF'
        mock_resource.resource_type = None
        mock_resource.description = 'Description'
        mock_resource.is_active = True
        mock_resource.max_concurrent_events = 1
        mock_resource.location_id = None
        mock_resource.location = None
        mock_resource.is_mobile = False
        mock_resource.user_id = None
        mock_resource.user = None

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[mock_resource])

        mock_resource_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        result = api.get_resources()

        assert isinstance(result, list)


class TestSafeScriptAPIGetResourceAvailability:
    """Tests for SafeScriptAPI.get_resource_availability method."""

    def test_has_get_resource_availability_method(self):
        """Should have get_resource_availability method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_resource_availability')
        assert callable(api.get_resource_availability)


class TestSafeScriptAPIGetServices:
    """Tests for SafeScriptAPI.get_services method."""

    def test_has_get_services_method(self):
        """Should have get_services method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_services')
        assert callable(api.get_services)


class TestSafeScriptAPIGetServiceStats:
    """Tests for SafeScriptAPI.get_service_stats method."""

    def test_has_get_service_stats_method(self):
        """Should have get_service_stats method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_service_stats')
        assert callable(api.get_service_stats)


class TestSafeScriptAPIGetPayments:
    """Tests for SafeScriptAPI.get_payments method."""

    def test_has_get_payments_method(self):
        """Should have get_payments method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_payments')
        assert callable(api.get_payments)

    def test_requires_payment_processing_feature(self):
        """Should require payment_processing feature."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=False)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.get_payments()

        assert 'not available on your plan' in str(exc_info.value)


class TestSafeScriptAPIGetInvoices:
    """Tests for SafeScriptAPI.get_invoices method."""

    def test_has_get_invoices_method(self):
        """Should have get_invoices method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_invoices')
        assert callable(api.get_invoices)


class TestSafeScriptAPIGetRevenueStats:
    """Tests for SafeScriptAPI.get_revenue_stats method."""

    def test_has_get_revenue_stats_method(self):
        """Should have get_revenue_stats method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_revenue_stats')
        assert callable(api.get_revenue_stats)


class TestSafeScriptAPIGetContracts:
    """Tests for SafeScriptAPI.get_contracts method."""

    def test_has_get_contracts_method(self):
        """Should have get_contracts method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_contracts')
        assert callable(api.get_contracts)

    def test_requires_contracts_feature(self):
        """Should require can_use_contracts feature."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=False)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.get_contracts()

        assert 'not available on your plan' in str(exc_info.value)


class TestSafeScriptAPIGetExpiringContracts:
    """Tests for SafeScriptAPI.get_expiring_contracts method."""

    def test_has_get_expiring_contracts_method(self):
        """Should have get_expiring_contracts method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_expiring_contracts')
        assert callable(api.get_expiring_contracts)


class TestSafeScriptAPIGetLocations:
    """Tests for SafeScriptAPI.get_locations method."""

    def test_has_get_locations_method(self):
        """Should have get_locations method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_locations')
        assert callable(api.get_locations)


class TestSafeScriptAPIGetLocationStats:
    """Tests for SafeScriptAPI.get_location_stats method."""

    def test_has_get_location_stats_method(self):
        """Should have get_location_stats method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_location_stats')
        assert callable(api.get_location_stats)

    def test_requires_multi_location_feature(self):
        """Should require multi_location feature."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=False)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.get_location_stats(1)

        assert 'not available on your plan' in str(exc_info.value)


class TestSafeScriptAPIGetStaff:
    """Tests for SafeScriptAPI.get_staff method."""

    def test_has_get_staff_method(self):
        """Should have get_staff method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_staff')
        assert callable(api.get_staff)


class TestSafeScriptAPIGetStaffPerformance:
    """Tests for SafeScriptAPI.get_staff_performance method."""

    def test_has_get_staff_performance_method(self):
        """Should have get_staff_performance method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_staff_performance')
        assert callable(api.get_staff_performance)


class TestSafeScriptAPICreateVideoMeeting:
    """Tests for SafeScriptAPI.create_video_meeting method."""

    def test_has_create_video_meeting_method(self):
        """Should have create_video_meeting method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'create_video_meeting')
        assert callable(api.create_video_meeting)

    def test_requires_video_conferencing_feature(self):
        """Should require can_add_video_conferencing feature."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=False)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.create_video_meeting()

        assert 'not available on your plan' in str(exc_info.value)

    def test_validates_provider(self):
        """Should validate video provider."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=True)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.create_video_meeting(provider='invalid_provider')

        assert 'Invalid video provider' in str(exc_info.value)

    def test_returns_meeting_data(self):
        """Should return meeting data."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=True)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        result = api.create_video_meeting(provider='zoom', title='Test Meeting')

        assert 'meeting_id' in result
        assert 'join_url' in result
        assert 'host_url' in result
        assert result['provider'] == 'zoom'
        assert result['title'] == 'Test Meeting'


class TestSafeScriptAPIGetSystemEmailTypes:
    """Tests for SafeScriptAPI.get_system_email_types method."""

    def test_has_get_system_email_types_method(self):
        """Should have get_system_email_types method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_system_email_types')
        assert callable(api.get_system_email_types)

    def test_returns_list_of_email_types(self):
        """Should return list of available email types."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        result = api.get_system_email_types()

        assert isinstance(result, list)
        # Should have at least some email types
        assert len(result) > 0
        # Each item should have required fields
        for item in result:
            assert 'type' in item
            assert 'display_name' in item
            assert 'description' in item
            assert 'category' in item


class TestSafeScriptAPISendSystemEmail:
    """Tests for SafeScriptAPI.send_system_email method."""

    def test_has_send_system_email_method(self):
        """Should have send_system_email method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'send_system_email')
        assert callable(api.send_system_email)

    def test_returns_false_when_template_not_found(self):
        """Should return False when no template exists for email type."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})

        # Use a mock to simulate no template found
        with patch('smoothschedule.communication.messaging.models.PuckEmailTemplate') as mock_model:
            mock_model.objects.filter.return_value.first.return_value = None

            result = api.send_system_email(
                email_type='nonexistent_type',
                to='test@example.com'
            )
            assert result is False


class TestSafeScriptAPIGetEmailTemplates:
    """Tests for SafeScriptAPI.get_email_templates method (DEPRECATED)."""

    def test_has_get_email_templates_method(self):
        """Should have get_email_templates method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_email_templates')
        assert callable(api.get_email_templates)

    def test_returns_empty_list_deprecated(self):
        """Deprecated method should return empty list."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        result = api.get_email_templates()
        assert result == []


class TestSafeScriptAPISendTemplateEmail:
    """Tests for SafeScriptAPI.send_template_email method (DEPRECATED)."""

    def test_has_send_template_email_method(self):
        """Should have send_template_email method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'send_template_email')
        assert callable(api.send_template_email)

    def test_returns_false_deprecated(self):
        """Deprecated method should return False."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        result = api.send_template_email(template_id=1, to='test@example.com')
        assert result is False


class TestSafeScriptAPIGetAnalytics:
    """Tests for SafeScriptAPI.get_analytics method."""

    def test_has_get_analytics_method(self):
        """Should have get_analytics method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_analytics')
        assert callable(api.get_analytics)

    def test_requires_advanced_reporting_feature(self):
        """Should require advanced_reporting feature."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=False)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.get_analytics()

        assert 'not available on your plan' in str(exc_info.value)


class TestSafeScriptAPIGetBookingTrends:
    """Tests for SafeScriptAPI.get_booking_trends method."""

    def test_has_get_booking_trends_method(self):
        """Should have get_booking_trends method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_booking_trends')
        assert callable(api.get_booking_trends)


class TestSafeScriptAPIUpdateAppointment:
    """Tests for SafeScriptAPI.update_appointment method."""

    def test_has_update_appointment_method(self):
        """Should have update_appointment method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'update_appointment')
        assert callable(api.update_appointment)


class TestSafeScriptAPIGetRecurringAppointments:
    """Tests for SafeScriptAPI.get_recurring_appointments method."""

    def test_has_get_recurring_appointments_method(self):
        """Should have get_recurring_appointments method."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        assert hasattr(api, 'get_recurring_appointments')
        assert callable(api.get_recurring_appointments)


class TestSafeScriptAPIGetAppointmentsFilters:
    """Tests for comprehensive filtering in get_appointments method."""

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_start_time_gt(self, mock_event_class):
        """Should filter by start_time greater than."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(start_time__gt='2024-01-01T10:00:00')

        # Should have called filter with start_time__gt
        filter_calls = [str(c) for c in mock_queryset.filter.call_args_list]
        assert any('start_time__gt' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_start_time_lt(self, mock_event_class):
        """Should filter by start_time less than."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(start_time__lt='2024-01-01T10:00:00')

        assert any('start_time__lt' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_start_time_gte(self, mock_event_class):
        """Should filter by start_time greater than or equal."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(start_time__gte='2024-01-01T10:00:00')

        assert any('start_time__gte' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_start_time_lte(self, mock_event_class):
        """Should filter by start_time less than or equal."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(start_time__lte='2024-01-01T10:00:00')

        assert any('start_time__lte' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_end_time_gt(self, mock_event_class):
        """Should filter by end_time greater than."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(end_time__gt='2024-01-01T10:00:00')

        assert any('end_time__gt' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_end_time_lt(self, mock_event_class):
        """Should filter by end_time less than."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(end_time__lt='2024-01-01T10:00:00')

        assert any('end_time__lt' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_created_at_gte(self, mock_event_class):
        """Should filter by created_at greater than or equal."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(created_at__gte='2024-01-01T10:00:00')

        assert any('created_at__gte' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_updated_at_gte(self, mock_event_class):
        """Should filter by updated_at greater than or equal."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(updated_at__gte='2024-01-01T10:00:00')

        assert any('updated_at__gte' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_service_id(self, mock_event_class):
        """Should filter by service_id."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(service_id=123)

        assert any('service_id' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_location_id(self, mock_event_class):
        """Should filter by location_id."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(location_id=456)

        assert any('location_id' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_title_contains(self, mock_event_class):
        """Should filter by title contains (case-insensitive)."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(title__icontains='meeting')

        assert any('title__icontains' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_notes_contains(self, mock_event_class):
        """Should filter by notes contains (case-insensitive)."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(notes__icontains='important')

        assert any('notes__icontains' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_customer_id(self, mock_event_class):
        """Should filter by customer_id (via participants)."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.distinct.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(customer_id=789)

        assert any('participants__user_id' in str(c) or 'customer' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_resource_id(self, mock_event_class):
        """Should filter by resource_id (via participants)."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.distinct.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(resource_id=321)

        assert any('participants__resource_id' in str(c) or 'resource' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_deposit_amount_gte(self, mock_event_class):
        """Should filter by deposit_amount greater than or equal."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(deposit_amount__gte=50.00)

        assert any('deposit_amount__gte' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_final_price_lte(self, mock_event_class):
        """Should filter by final_price less than or equal."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(final_price__lte=100.00)

        assert any('final_price__lte' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_has_deposit(self, mock_event_class):
        """Should filter appointments that have a deposit."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.exclude.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(has_deposit=True)

        # Should use exclude(deposit_amount__isnull=True) or filter(deposit_amount__isnull=False)
        call_args_str = str(mock_queryset.filter.call_args_list) + str(mock_queryset.exclude.call_args_list)
        assert 'deposit_amount' in call_args_str

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_by_multiple_statuses(self, mock_event_class):
        """Should filter by multiple statuses using __in."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(status__in=['SCHEDULED', 'COMPLETED'])

        assert any('status__in' in str(c) for c in mock_queryset.filter.call_args_list)

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_filter_combination(self, mock_event_class):
        """Should support combining multiple filters."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        api.get_appointments(
            status='SCHEDULED',
            start_time__gte='2024-01-01T00:00:00',
            start_time__lt='2024-02-01T00:00:00',
            service_id=123
        )

        # Multiple filter calls should have been made
        assert mock_queryset.filter.call_count >= 1

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_returns_comprehensive_data(self, mock_event_class):
        """Should return comprehensive appointment data including related objects."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI
        from datetime import datetime

        mock_service = Mock()
        mock_service.id = 1
        mock_service.name = 'Haircut'

        mock_location = Mock()
        mock_location.id = 2
        mock_location.name = 'Main Office'

        mock_event = Mock()
        mock_event.id = 100
        mock_event.title = 'Test Appointment'
        mock_event.start_time = datetime(2024, 1, 15, 10, 0, 0)
        mock_event.end_time = datetime(2024, 1, 15, 11, 0, 0)
        mock_event.status = 'SCHEDULED'
        mock_event.notes = 'Some notes'
        mock_event.created_at = datetime(2024, 1, 1, 9, 0, 0)
        mock_event.updated_at = datetime(2024, 1, 1, 9, 0, 0)
        mock_event.service = mock_service
        mock_event.service_id = 1
        mock_event.location = mock_location
        mock_event.location_id = 2
        mock_event.deposit_amount = 25.00
        mock_event.final_price = None

        mock_queryset = MagicMock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.__getitem__ = Mock(return_value=[mock_event])
        mock_event_class.objects.all.return_value = mock_queryset

        api = SafeScriptAPI(business=Mock(), user=Mock(), execution_context={})
        result = api.get_appointments()

        assert len(result) == 1
        appt = result[0]
        assert appt['id'] == 100
        assert appt['title'] == 'Test Appointment'
        assert 'service_id' in appt
        assert 'service_name' in appt
        assert 'location_id' in appt
        assert 'location_name' in appt
        assert 'created_at' in appt
        assert 'updated_at' in appt
        assert 'deposit_amount' in appt
        assert 'final_price' in appt

    def test_requires_recurring_appointments_feature(self):
        """Should require recurring_appointments feature."""
        from smoothschedule.scheduling.schedule.safe_scripting import SafeScriptAPI, ScriptExecutionError

        mock_business = Mock()
        mock_business.has_feature = Mock(return_value=False)

        api = SafeScriptAPI(business=mock_business, user=Mock(), execution_context={})

        with pytest.raises(ScriptExecutionError) as exc_info:
            api.get_recurring_appointments()

        assert 'not available on your plan' in str(exc_info.value)
