"""
Tests for the CLI module.
"""

import pytest
import argparse
from unittest.mock import Mock, patch, MagicMock
from esppal.esppal import (
    _parse_vidpid, _handle_status_command, _handle_daemon_command
)
from esppal.client import DaemonNotRunning
from esppal.protocol import StatusMessage


class TestParseVidPid:
    """Tests for VID/PID parsing."""

    def test_parse_from_vidpid_arg(self):
        """Parse from --vidpid argument."""
        args = argparse.Namespace(vid=None, pid=None, vidpid="10c4:ea60")
        vid, pid = _parse_vidpid(args)
        assert vid == "10c4"
        assert pid == "ea60"

    def test_parse_from_separate_args(self):
        """Parse from --vid and --pid arguments."""
        args = argparse.Namespace(vid="10c4", pid="ea60", vidpid=None)
        vid, pid = _parse_vidpid(args)
        assert vid == "10c4"
        assert pid == "ea60"

    def test_vidpid_overrides_separate(self):
        """--vidpid overrides --vid/--pid."""
        args = argparse.Namespace(vid="aaaa", pid="bbbb", vidpid="10c4:ea60")
        vid, pid = _parse_vidpid(args)
        assert vid == "10c4"
        assert pid == "ea60"

    def test_no_vidpid(self):
        """No VID/PID arguments."""
        args = argparse.Namespace(vid=None, pid=None, vidpid=None)
        vid, pid = _parse_vidpid(args)
        assert vid is None
        assert pid is None

    def test_missing_vidpid_attr(self):
        """Handle missing vidpid attribute."""
        args = argparse.Namespace(vid="10c4", pid="ea60")
        vid, pid = _parse_vidpid(args)
        assert vid == "10c4"
        assert pid == "ea60"


class TestHandleStatusCommand:
    """Tests for status command handler."""

    def test_status_shows_info(self):
        """Status command displays daemon info."""
        args = argparse.Namespace(vid=None, pid=None, vidpid=None, device=None)

        mock_status = StatusMessage(
            port="/dev/ttyUSB0",
            port_state="connected",
            clients=2,
            buffer_bytes=1024,
            buffer_entries=10,
            uptime=123.4,
            idle_timeout=1776.6,
            vid_pid="10c4:ea60"
        )

        with patch('esppal.esppal.ESPalClient') as MockClient:
            client = MockClient.return_value
            client.status.return_value = mock_status
            client.disconnect.return_value = None

            result = _handle_status_command(args)

            assert result == 0
            client.status.assert_called_once()

    def test_status_daemon_not_running(self):
        """Status shows error when daemon not running."""
        args = argparse.Namespace(vid=None, pid=None, vidpid=None, device=None)

        with patch('esppal.esppal.ESPalClient') as MockClient:
            client = MockClient.return_value
            client.status.return_value = None
            client.disconnect.return_value = None

            result = _handle_status_command(args)

            assert result == 1

    def test_status_daemon_exception(self):
        """Status handles DaemonNotRunning exception."""
        args = argparse.Namespace(vid=None, pid=None, vidpid=None, device=None)

        with patch('esppal.esppal.ESPalClient') as MockClient:
            client = MockClient.return_value
            client.status.side_effect = DaemonNotRunning("Not running")
            client.disconnect.return_value = None

            result = _handle_status_command(args)

            assert result == 1


class TestHandleDaemonCommand:
    """Tests for daemon command handler."""

    def test_daemon_stop(self):
        """Daemon stop sends shutdown request."""
        args = argparse.Namespace(
            vid=None, pid=None, vidpid=None, device=None,
            daemon_command='stop'
        )

        with patch('esppal.esppal.ESPalClient') as MockClient:
            client = MockClient.return_value
            client.shutdown.return_value = True
            client.disconnect.return_value = None

            result = _handle_daemon_command(args)

            assert result == 0
            client.shutdown.assert_called_once()

    def test_daemon_stop_fails(self):
        """Daemon stop returns error when shutdown fails."""
        args = argparse.Namespace(
            vid=None, pid=None, vidpid=None, device=None,
            daemon_command='stop'
        )

        with patch('esppal.esppal.ESPalClient') as MockClient:
            client = MockClient.return_value
            client.shutdown.return_value = False
            client.disconnect.return_value = None

            result = _handle_daemon_command(args)

            assert result == 1

    def test_daemon_no_subcommand(self):
        """Daemon with no subcommand shows usage."""
        args = argparse.Namespace(
            vid=None, pid=None, vidpid=None, device=None,
            daemon_command=None
        )

        result = _handle_daemon_command(args)

        assert result == 1

    def test_daemon_status_delegates(self):
        """Daemon status delegates to status handler."""
        args = argparse.Namespace(
            vid=None, pid=None, vidpid=None, device=None,
            daemon_command='status'
        )

        mock_status = StatusMessage(
            port="/dev/ttyUSB0",
            port_state="connected",
            clients=1,
            buffer_bytes=512,
            buffer_entries=5,
            uptime=50.0,
            idle_timeout=1750.0,
            vid_pid="10c4:ea60"
        )

        with patch('esppal.esppal.ESPalClient') as MockClient:
            client = MockClient.return_value
            client.status.return_value = mock_status
            client.disconnect.return_value = None

            result = _handle_daemon_command(args)

            assert result == 0


class TestMonitorArgParsing:
    """Tests for monitor argument parsing."""

    def test_monitor_has_since_upload_flag(self):
        """Monitor parser has --since-upload flag."""
        from esppal.esppal import main
        import sys

        # We can't easily test the full main() but we can verify the
        # argument parsing by checking help output contains the flags
        with patch.object(sys, 'argv', ['esppal', 'monitor', '--help']):
            with pytest.raises(SystemExit) as exc:
                main()
            # --help exits with 0
            assert exc.value.code == 0

    def test_monitor_has_tail_flag(self):
        """Monitor parser has --tail flag."""
        from esppal.esppal import main
        import sys

        with patch.object(sys, 'argv', ['esppal', 'monitor', '--help']):
            with pytest.raises(SystemExit) as exc:
                main()
            assert exc.value.code == 0

    def test_monitor_has_replay_flag(self):
        """Monitor parser has --replay flag."""
        from esppal.esppal import main
        import sys

        with patch.object(sys, 'argv', ['esppal', 'monitor', '--help']):
            with pytest.raises(SystemExit) as exc:
                main()
            assert exc.value.code == 0

    def test_monitor_has_legacy_flag(self):
        """Monitor parser has --legacy flag."""
        from esppal.esppal import main
        import sys

        with patch.object(sys, 'argv', ['esppal', 'monitor', '--help']):
            with pytest.raises(SystemExit) as exc:
                main()
            assert exc.value.code == 0
