"""Tests for the process registry module."""

from __future__ import annotations

import time

import pytest

from elizaos_plugin_shell.process_registry import (
    FinishedSession,
    ProcessRegistry,
    ProcessSession,
    ProcessStatus,
    _create_session_slug,
    _tail,
    _trim_with_cap,
)


class TestProcessRegistry:
    def setup_method(self):
        self.registry = ProcessRegistry()

    def teardown_method(self):
        self.registry.reset_for_tests()

    def test_register_and_get(self):
        session = ProcessSession(id="test-1", command="echo hello")
        self.registry.register_process(session)
        retrieved = self.registry.get_process("test-1")
        assert retrieved is not None
        assert retrieved.command == "echo hello"

    def test_get_nonexistent(self):
        assert self.registry.get_process("nonexistent") is None

    def test_delete_session(self):
        session = ProcessSession(id="test-1", command="echo hello")
        self.registry.register_process(session)
        self.registry.delete_session("test-1")
        assert self.registry.get_process("test-1") is None

    def test_list_processes_only_backgrounded(self):
        s1 = ProcessSession(id="bg-1", command="cmd1", backgrounded=True)
        s2 = ProcessSession(id="fg-1", command="cmd2", backgrounded=False)
        self.registry.register_process(s1)
        self.registry.register_process(s2)

        listed = self.registry.list_processes()
        assert len(listed) == 1
        assert listed[0].id == "bg-1"

    def test_list_all(self):
        s1 = ProcessSession(id="bg-1", command="cmd1", backgrounded=True)
        self.registry.register_process(s1)
        self.registry.mark_exited(s1, 0, None, ProcessStatus.COMPLETED)

        result = self.registry.list_all()
        assert len(result["finished"]) == 1

    def test_mark_exited(self):
        session = ProcessSession(id="test-1", command="echo hello", backgrounded=True)
        self.registry.register_process(session)
        self.registry.mark_exited(session, 0, None, ProcessStatus.COMPLETED)

        assert self.registry.get_process("test-1") is None
        finished = self.registry.get_finished("test-1")
        assert finished is not None
        assert finished.exit_code == 0
        assert finished.status == ProcessStatus.COMPLETED

    def test_mark_exited_non_backgrounded(self):
        session = ProcessSession(id="test-1", command="echo hello", backgrounded=False)
        self.registry.register_process(session)
        self.registry.mark_exited(session, 0, None, ProcessStatus.COMPLETED)

        assert self.registry.get_process("test-1") is None
        # Non-backgrounded sessions should not be in finished
        assert self.registry.get_finished("test-1") is None

    def test_mark_backgrounded(self):
        session = ProcessSession(id="test-1", command="echo hello")
        self.registry.register_process(session)
        assert session.backgrounded is False
        self.registry.mark_backgrounded(session)
        assert session.backgrounded is True

    def test_append_output(self):
        session = ProcessSession(id="test-1", command="echo hello")
        self.registry.register_process(session)

        self.registry.append_output(session, "stdout", "hello world\n")
        assert "hello world" in session.aggregated
        assert session.total_output_chars == len("hello world\n")

    def test_drain_session(self):
        session = ProcessSession(id="test-1", command="echo hello")
        self.registry.register_process(session)

        self.registry.append_output(session, "stdout", "hello")
        self.registry.append_output(session, "stderr", "error")

        drained = self.registry.drain_session(session)
        assert drained["stdout"] == "hello"
        assert drained["stderr"] == "error"

        # Second drain should be empty
        drained2 = self.registry.drain_session(session)
        assert drained2["stdout"] == ""
        assert drained2["stderr"] == ""

    def test_clear_finished(self):
        session = ProcessSession(id="test-1", command="cmd", backgrounded=True)
        self.registry.register_process(session)
        self.registry.mark_exited(session, 0, None, ProcessStatus.COMPLETED)

        assert self.registry.get_finished("test-1") is not None
        self.registry.clear_finished()
        assert self.registry.get_finished("test-1") is None

    def test_create_session_slug_unique(self):
        slug1 = self.registry.create_session_slug()
        session = ProcessSession(id=slug1, command="cmd")
        self.registry.register_process(session)

        slug2 = self.registry.create_session_slug()
        assert slug1 != slug2

    def test_set_job_ttl(self):
        self.registry.set_job_ttl_ms(120_000)
        # Should not crash

    def test_kill_process_no_pid(self):
        session = ProcessSession(id="test-1", command="cmd", pid=None)
        self.registry.register_process(session)
        assert self.registry.kill_process("test-1") is False

    def test_kill_process_nonexistent(self):
        assert self.registry.kill_process("nonexistent") is False

    def test_cleanup_dead_no_sessions(self):
        cleaned = self.registry.cleanup_dead()
        assert cleaned == 0


class TestUtilityFunctions:
    def test_tail_short(self):
        assert _tail("hello", 10) == "hello"

    def test_tail_long(self):
        text = "a" * 5000
        result = _tail(text, 2000)
        assert len(result) == 2000
        assert result == "a" * 2000

    def test_trim_with_cap(self):
        text = "a" * 1000
        result = _trim_with_cap(text, 500)
        assert len(result) == 500

    def test_trim_with_cap_short(self):
        text = "hello"
        assert _trim_with_cap(text, 500) == "hello"

    def test_create_session_slug(self):
        slug = _create_session_slug()
        assert "-" in slug
        parts = slug.split("-")
        assert len(parts) >= 2

    def test_create_session_slug_avoids_taken(self):
        taken = set()

        def is_taken(s):
            return s in taken

        slug1 = _create_session_slug(is_taken)
        taken.add(slug1)
        slug2 = _create_session_slug(is_taken)
        assert slug1 != slug2
