# Copyright (c) 2026 Pyry Haulos
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT

"""Integration tests for container resource limits.

Tests the resource limits configuration flow end-to-end:
1. Server config specifies per-repo resource_limits
2. Resource limits are threaded to sandbox.create_task()
3. Task applies limits as podman flags

Since integration tests use mock_podman (which wraps `uv run`), we cannot
verify actual cgroup enforcement.  Instead, we verify that:
- Resource limits from server config are threaded through to create_task
- Per-repo limits (including those resolved from !var) are applied
"""

import sys
import threading
from pathlib import Path


sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))

from airut.sandbox.types import ResourceLimits

from .conftest import (
    get_message_text,
    wait_for_conv_completion,
)
from .environment import IntegrationEnvironment


class TestResourceLimits:
    """Test resource limits configuration end-to-end."""

    def test_resource_limits_passed_to_create_task(
        self,
        integration_env: IntegrationEnvironment,
        create_email,
        extract_conversation_id,
    ) -> None:
        """Resource limits from server config are passed to create_task.

        Sets resource_limits on the RepoServerConfig, then verifies
        that sandbox.create_task() receives a ResourceLimits object with
        the configured values.
        """
        msg = create_email(
            subject="Resource limits test",
            body="Hello, world!",
        )
        integration_env.email_server.inject_message(msg)

        service = integration_env.create_service()

        # Set resource limits on the repo handler's config
        repo_handler = service.repo_handlers["test"]
        object.__setattr__(
            repo_handler.config,
            "resource_limits",
            ResourceLimits(timeout=120, memory="2g", cpus=1.5, pids_limit=256),
        )

        # Spy on create_task to capture the resource_limits argument
        original_create_task = service.sandbox.create_task
        captured_limits: list[ResourceLimits] = []

        def spy_create_task(**kwargs):
            rl = kwargs.get("resource_limits")
            if rl is not None:
                captured_limits.append(rl)
            return original_create_task(**kwargs)

        service.sandbox.create_task = spy_create_task

        service_thread = threading.Thread(target=service.start, daemon=True)
        service_thread.start()

        try:
            # Wait for acknowledgment
            ack = integration_env.email_server.wait_for_sent(
                lambda m: "started working" in get_message_text(m).lower(),
                timeout=15.0,
            )
            assert ack is not None, "Did not receive acknowledgment email"
            conv_id = extract_conversation_id(ack["Subject"])
            assert conv_id is not None

            # Wait for completion
            task = wait_for_conv_completion(
                service.tracker, conv_id, timeout=30.0
            )
            assert task is not None

            # Verify resource limits were captured
            assert len(captured_limits) >= 1
            rl = captured_limits[0]
            assert rl.timeout == 120
            assert rl.memory == "2g"
            assert rl.cpus == 1.5
            assert rl.pids_limit == 256

        finally:
            service.stop()
            service_thread.join(timeout=10.0)

    def test_partial_resource_limits(
        self,
        integration_env: IntegrationEnvironment,
        create_email,
        extract_conversation_id,
    ) -> None:
        """Partial resource_limits are threaded through correctly.

        Sets per-repo timeout=7200 and memory="16g" only (no cpus or
        pids_limit).  Verifies the set values are passed and unset
        fields remain None.
        """
        msg = create_email(
            subject="Partial limits test",
            body="Hello, world!",
        )
        integration_env.email_server.inject_message(msg)

        service = integration_env.create_service()

        repo_handler = service.repo_handlers["test"]
        object.__setattr__(
            repo_handler.config,
            "resource_limits",
            ResourceLimits(timeout=7200, memory="16g"),
        )

        original_create_task = service.sandbox.create_task
        captured_limits: list[ResourceLimits] = []

        def spy_create_task(**kwargs):
            rl = kwargs.get("resource_limits")
            if rl is not None:
                captured_limits.append(rl)
            return original_create_task(**kwargs)

        service.sandbox.create_task = spy_create_task

        service_thread = threading.Thread(target=service.start, daemon=True)
        service_thread.start()

        try:
            ack = integration_env.email_server.wait_for_sent(
                lambda m: "started working" in get_message_text(m).lower(),
                timeout=15.0,
            )
            assert ack is not None
            conv_id = extract_conversation_id(ack["Subject"])
            assert conv_id is not None

            task = wait_for_conv_completion(
                service.tracker, conv_id, timeout=30.0
            )
            assert task is not None
            assert len(captured_limits) >= 1
            rl = captured_limits[0]
            assert rl.timeout == 7200
            assert rl.memory == "16g"
            assert rl.cpus is None
            assert rl.pids_limit is None

        finally:
            service.stop()
            service_thread.join(timeout=10.0)
