"""Tests for services:logs (non-follow mode) — SDK layer."""

from __future__ import annotations

import pytest

from kuberoku.exceptions import (
    AppNotFoundError,
    DynoNotFoundError,
    NoDynosError,
)
from kuberoku.factory import KuberokuFactory
from kuberoku.models import LogLine
from tests.backends.fake import FakeK8sClient
from tests.services.conftest import _create_fake_pod


class TestGetLogs:
    def test_returns_log_lines(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        lines = deployed_factory_with_pods.services.get_logs("myapp")
        assert len(lines) > 0
        assert all(isinstance(x, LogLine) for x in lines)

    def test_log_lines_have_dyno_names(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        lines = deployed_factory_with_pods.services.get_logs("myapp")
        dynos = {x.dyno for x in lines}
        assert "web.1" in dynos
        assert "web.2" in dynos

    def test_log_source_is_app(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        lines = deployed_factory_with_pods.services.get_logs("myapp")
        assert all(x.source == "app" for x in lines)

    def test_log_messages_match_injected(
        self, deployed_factory_with_pods: KuberokuFactory
    ) -> None:
        lines = deployed_factory_with_pods.services.get_logs("myapp")
        messages = [x.message for x in lines]
        assert "Starting server..." in messages
        assert "Listening on :8080" in messages
        assert "Request handled" in messages

    def test_filter_by_dyno(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        lines = deployed_factory_with_pods.services.get_logs("myapp", dyno="web.1")
        assert all(x.dyno == "web.1" for x in lines)
        assert len(lines) > 0

    def test_filter_by_process_type(
        self, multi_process_factory_with_pods: KuberokuFactory
    ) -> None:
        lines = multi_process_factory_with_pods.services.get_logs("myapp", process_type="worker")
        assert all(x.dyno.startswith("worker.") for x in lines)
        assert len(lines) > 0

    def test_num_limits_lines(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        # Pod-a has 2 lines, pod-b has 3 lines. With num=1, each pod returns its last line.
        lines = deployed_factory_with_pods.services.get_logs("myapp", num=1)
        assert len(lines) == 2  # 2 pods × 1 line each
        messages = {x.message for x in lines}
        # Each pod's last line is "Listening on :8080" (pod-a) and "Request handled" (pod-b)
        assert "Listening on :8080" in messages
        assert "Request handled" in messages

    def test_app_not_found_raises(self, tmp_namespace: str) -> None:
        factory = KuberokuFactory(k8s_client=FakeK8sClient(), namespace=tmp_namespace)
        with pytest.raises(AppNotFoundError):
            factory.services.get_logs("nonexistent")

    def test_no_pods_raises(self, deployed_factory: KuberokuFactory) -> None:
        # deployed_factory has deployment but no fake pods
        with pytest.raises(NoDynosError):
            deployed_factory.services.get_logs("myapp")

    def test_dyno_not_found_raises(self, deployed_factory_with_pods: KuberokuFactory) -> None:
        with pytest.raises(DynoNotFoundError):
            deployed_factory_with_pods.services.get_logs("myapp", dyno="web.99")

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

        lines = deployed_factory_with_pods.services.get_logs("myapp", since="5m")
        assert len(lines) > 0
        # Verify since_seconds=300 was passed to every stream_pod_logs call
        assert len(k8s.log_calls) > 0
        for call in k8s.log_calls:
            assert call["since_seconds"] == 300
            assert call["follow"] is False

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

        lines = deployed_factory_with_pods.services.get_logs("myapp", previous=True)
        assert len(lines) > 0
        # Verify previous=True was passed to every stream_pod_logs call
        assert len(k8s.log_calls) > 0
        for call in k8s.log_calls:
            assert call["previous"] is True

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

        deployed_factory_with_pods.services.get_logs("myapp", num=42)
        assert len(k8s.log_calls) > 0
        for call in k8s.log_calls:
            assert call["tail_lines"] == 42


class TestDynoNaming:
    def test_single_pod_is_type_dot_1(self, deployed_factory: KuberokuFactory) -> None:
        _create_fake_pod(
            deployed_factory,
            "myapp",
            "web",
            "only-pod",
            log_lines=["hello"],
        )
        lines = deployed_factory.services.get_logs("myapp")
        assert lines[0].dyno == "web.1"

    def test_pods_sorted_by_name(self, deployed_factory: KuberokuFactory) -> None:
        _create_fake_pod(deployed_factory, "myapp", "web", "zzz", log_lines=["z"])
        _create_fake_pod(deployed_factory, "myapp", "web", "aaa", log_lines=["a"])
        lines = deployed_factory.services.get_logs("myapp")
        # aaa sorts first → web.1, zzz → web.2
        web1_lines = [x for x in lines if x.dyno == "web.1"]
        assert web1_lines[0].message == "a"

    def test_multi_type_dyno_names(self, multi_process_factory_with_pods: KuberokuFactory) -> None:
        lines = multi_process_factory_with_pods.services.get_logs("myapp")
        dynos = {x.dyno for x in lines}
        assert "web.1" in dynos
        assert "worker.1" in dynos
