"""Tests for services:logs follow mode — SDK layer."""

from __future__ import annotations

import threading

import pytest

from kuberoku.exceptions import NoDynosError
from kuberoku.factory import KuberokuFactory
from kuberoku.models import LogLine
from tests.backends.fake import FakeK8sClient


class TestStreamLogs:
    def test_follow_delivers_injected_content(
        self, deployed_factory_with_pods: KuberokuFactory
    ) -> None:
        collected: list[LogLine] = []
        stop = threading.Event()

        def on_line(line: LogLine) -> None:
            collected.append(line)
            # Pod-a has 2 lines, pod-b has 3 → expect 5 total
            if len(collected) >= 5:
                stop.set()

        t = threading.Thread(
            target=deployed_factory_with_pods.services.stream_logs,
            kwargs={
                "app": "myapp",
                "on_line": on_line,
                "stop_event": stop,
            },
        )
        t.start()
        t.join(timeout=5.0)
        if not stop.is_set():
            stop.set()
        t.join(timeout=2.0)

        assert len(collected) > 0
        assert all(isinstance(x, LogLine) for x in collected)
        # Verify actual injected messages appear
        messages = {x.message for x in collected}
        assert "Starting server..." in messages
        assert "Listening on :8080" in messages

    def test_follow_has_dyno_names(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        collected: list[LogLine] = []
        stop = threading.Event()

        def on_line(line: LogLine) -> None:
            collected.append(line)
            if len(collected) >= 4:
                stop.set()

        t = threading.Thread(
            target=deployed_factory_with_pods.services.stream_logs,
            kwargs={
                "app": "myapp",
                "on_line": on_line,
                "stop_event": stop,
            },
        )
        t.start()
        t.join(timeout=5.0)
        if not stop.is_set():
            stop.set()
        t.join(timeout=2.0)

        dynos = {x.dyno for x in collected}
        assert any(d.startswith("web.") for d in dynos)

    def test_follow_filter_by_process_type(
        self, multi_process_factory_with_pods: KuberokuFactory
    ) -> None:
        collected: list[LogLine] = []
        stop = threading.Event()

        def on_line(line: LogLine) -> None:
            collected.append(line)
            if len(collected) >= 2:
                stop.set()

        t = threading.Thread(
            target=multi_process_factory_with_pods.services.stream_logs,
            kwargs={
                "app": "myapp",
                "process_type": "worker",
                "on_line": on_line,
                "stop_event": stop,
            },
        )
        t.start()
        t.join(timeout=5.0)
        if not stop.is_set():
            stop.set()
        t.join(timeout=2.0)

        assert all(x.dyno.startswith("worker.") for x in collected)

    def test_follow_no_pods_raises(self, deployed_factory: KuberokuFactory) -> None:
        with pytest.raises(NoDynosError):
            deployed_factory.services.stream_logs(app="myapp")

    def test_follow_passes_since_to_k8s(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        k8s = deployed_factory_with_pods.k8s
        assert isinstance(k8s, FakeK8sClient)
        k8s.log_calls.clear()

        stop = threading.Event()

        def on_line(line: LogLine) -> None:
            stop.set()

        t = threading.Thread(
            target=deployed_factory_with_pods.services.stream_logs,
            kwargs={
                "app": "myapp",
                "since": "1h",
                "on_line": on_line,
                "stop_event": stop,
            },
        )
        t.start()
        t.join(timeout=5.0)
        if not stop.is_set():
            stop.set()
        t.join(timeout=2.0)

        assert len(k8s.log_calls) > 0
        for call in k8s.log_calls:
            assert call["since_seconds"] == 3600
            assert call["follow"] is True

    def test_follow_stop_event_stops(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        stop = threading.Event()
        stop.set()  # Already set — should return immediately

        # Should not hang
        deployed_factory_with_pods.services.stream_logs(
            app="myapp",
            stop_event=stop,
        )
