# -*- coding: utf-8 -*-

"""
Unit tests for main.py CLI functions.
Tests for parse_cli_args(), resolve_server_config(), and print_startup_banner().
"""

import pytest
import argparse
import sys
from unittest.mock import patch, MagicMock
from io import StringIO


class TestParseCliArgs:
    """Tests for parse_cli_args() function."""
    
    def test_default_values_are_none(self):
        """
        What it does: Verifies that default values for host and port are None.
        Purpose: Ensure that None indicates "use env or default" in priority resolution.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with no arguments...")
        with patch.object(sys, 'argv', ['main.py']):
            args = parse_cli_args()
        
        print(f"args.host: {args.host}")
        print(f"args.port: {args.port}")
        print(f"Comparing: Expected host=None, port=None")
        assert args.host is None
        assert args.port is None
    
    def test_port_argument_long_form(self):
        """
        What it does: Verifies that --port argument is parsed correctly.
        Purpose: Ensure long form --port works.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with --port 9000...")
        with patch.object(sys, 'argv', ['main.py', '--port', '9000']):
            args = parse_cli_args()
        
        print(f"args.port: {args.port}")
        print(f"Comparing: Expected 9000, Got {args.port}")
        assert args.port == 9000
    
    def test_port_argument_short_form(self):
        """
        What it does: Verifies that -p argument is parsed correctly.
        Purpose: Ensure short form -p works.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with -p 8080...")
        with patch.object(sys, 'argv', ['main.py', '-p', '8080']):
            args = parse_cli_args()
        
        print(f"args.port: {args.port}")
        print(f"Comparing: Expected 8080, Got {args.port}")
        assert args.port == 8080
    
    def test_host_argument_long_form(self):
        """
        What it does: Verifies that --host argument is parsed correctly.
        Purpose: Ensure long form --host works.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with --host 127.0.0.1...")
        with patch.object(sys, 'argv', ['main.py', '--host', '127.0.0.1']):
            args = parse_cli_args()
        
        print(f"args.host: {args.host}")
        print(f"Comparing: Expected '127.0.0.1', Got '{args.host}'")
        assert args.host == "127.0.0.1"
    
    def test_host_argument_short_form(self):
        """
        What it does: Verifies that -H argument is parsed correctly.
        Purpose: Ensure short form -H works.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with -H 192.168.1.1...")
        with patch.object(sys, 'argv', ['main.py', '-H', '192.168.1.1']):
            args = parse_cli_args()
        
        print(f"args.host: {args.host}")
        print(f"Comparing: Expected '192.168.1.1', Got '{args.host}'")
        assert args.host == "192.168.1.1"
    
    def test_both_arguments_together(self):
        """
        What it does: Verifies that both --host and --port can be used together.
        Purpose: Ensure both arguments work simultaneously.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with --host 0.0.0.0 --port 3000...")
        with patch.object(sys, 'argv', ['main.py', '--host', '0.0.0.0', '--port', '3000']):
            args = parse_cli_args()
        
        print(f"args.host: {args.host}")
        print(f"args.port: {args.port}")
        assert args.host == "0.0.0.0"
        assert args.port == 3000
    
    def test_short_forms_together(self):
        """
        What it does: Verifies that both -H and -p can be used together.
        Purpose: Ensure short forms work simultaneously.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with -H 127.0.0.1 -p 5000...")
        with patch.object(sys, 'argv', ['main.py', '-H', '127.0.0.1', '-p', '5000']):
            args = parse_cli_args()
        
        print(f"args.host: {args.host}")
        print(f"args.port: {args.port}")
        assert args.host == "127.0.0.1"
        assert args.port == 5000


class TestResolveServerConfig:
    """Tests for resolve_server_config() function - priority hierarchy."""
    
    def test_cli_args_take_priority_over_env(self):
        """
        What it does: Verifies that CLI arguments have highest priority.
        Purpose: Ensure CLI args override environment variables.
        """
        print("Setup: Importing resolve_server_config...")
        from main import resolve_server_config
        
        print("Setup: Creating args with host=127.0.0.1, port=9000...")
        args = argparse.Namespace(host="127.0.0.1", port=9000)
        
        print("Action: Calling resolve_server_config with CLI args...")
        # Even if env vars are set, CLI should win
        with patch('main.SERVER_HOST', '0.0.0.0'), \
             patch('main.SERVER_PORT', 8000), \
             patch('main.DEFAULT_SERVER_HOST', '0.0.0.0'), \
             patch('main.DEFAULT_SERVER_PORT', 8000):
            host, port = resolve_server_config(args)
        
        print(f"Resolved host: {host}")
        print(f"Resolved port: {port}")
        print(f"Comparing: Expected ('127.0.0.1', 9000)")
        assert host == "127.0.0.1"
        assert port == 9000
    
    def test_env_vars_take_priority_over_defaults(self):
        """
        What it does: Verifies that env vars have priority over defaults.
        Purpose: Ensure env vars are used when CLI args are not provided.
        """
        print("Setup: Importing resolve_server_config...")
        from main import resolve_server_config
        
        print("Setup: Creating args with host=None, port=None (no CLI args)...")
        args = argparse.Namespace(host=None, port=None)
        
        print("Action: Calling resolve_server_config with env vars set...")
        # SERVER_HOST and SERVER_PORT are different from defaults
        with patch('main.SERVER_HOST', '192.168.1.100'), \
             patch('main.SERVER_PORT', 3000), \
             patch('main.DEFAULT_SERVER_HOST', '0.0.0.0'), \
             patch('main.DEFAULT_SERVER_PORT', 8000):
            host, port = resolve_server_config(args)
        
        print(f"Resolved host: {host}")
        print(f"Resolved port: {port}")
        print(f"Comparing: Expected ('192.168.1.100', 3000)")
        assert host == "192.168.1.100"
        assert port == 3000
    
    def test_defaults_used_when_nothing_set(self):
        """
        What it does: Verifies that defaults are used when nothing else is set.
        Purpose: Ensure default values work correctly.
        """
        print("Setup: Importing resolve_server_config...")
        from main import resolve_server_config
        
        print("Setup: Creating args with host=None, port=None...")
        args = argparse.Namespace(host=None, port=None)
        
        print("Action: Calling resolve_server_config with defaults...")
        # SERVER_HOST and SERVER_PORT equal to defaults (no env override)
        with patch('main.SERVER_HOST', '0.0.0.0'), \
             patch('main.SERVER_PORT', 8000), \
             patch('main.DEFAULT_SERVER_HOST', '0.0.0.0'), \
             patch('main.DEFAULT_SERVER_PORT', 8000):
            host, port = resolve_server_config(args)
        
        print(f"Resolved host: {host}")
        print(f"Resolved port: {port}")
        print(f"Comparing: Expected ('0.0.0.0', 8000)")
        assert host == "0.0.0.0"
        assert port == 8000
    
    def test_cli_host_only_env_port(self):
        """
        What it does: Verifies mixed priority - CLI host with env port.
        Purpose: Ensure each argument is resolved independently.
        """
        print("Setup: Importing resolve_server_config...")
        from main import resolve_server_config
        
        print("Setup: Creating args with host='127.0.0.1', port=None...")
        args = argparse.Namespace(host="127.0.0.1", port=None)
        
        print("Action: Calling resolve_server_config...")
        with patch('main.SERVER_HOST', '0.0.0.0'), \
             patch('main.SERVER_PORT', 9000), \
             patch('main.DEFAULT_SERVER_HOST', '0.0.0.0'), \
             patch('main.DEFAULT_SERVER_PORT', 8000):
            host, port = resolve_server_config(args)
        
        print(f"Resolved host: {host}")
        print(f"Resolved port: {port}")
        print(f"Comparing: Expected ('127.0.0.1', 9000)")
        assert host == "127.0.0.1"  # From CLI
        assert port == 9000  # From env (different from default)
    
    def test_cli_port_only_env_host(self):
        """
        What it does: Verifies mixed priority - CLI port with env host.
        Purpose: Ensure each argument is resolved independently.
        """
        print("Setup: Importing resolve_server_config...")
        from main import resolve_server_config
        
        print("Setup: Creating args with host=None, port=5000...")
        args = argparse.Namespace(host=None, port=5000)
        
        print("Action: Calling resolve_server_config...")
        with patch('main.SERVER_HOST', '192.168.1.1'), \
             patch('main.SERVER_PORT', 8000), \
             patch('main.DEFAULT_SERVER_HOST', '0.0.0.0'), \
             patch('main.DEFAULT_SERVER_PORT', 8000):
            host, port = resolve_server_config(args)
        
        print(f"Resolved host: {host}")
        print(f"Resolved port: {port}")
        print(f"Comparing: Expected ('192.168.1.1', 5000)")
        assert host == "192.168.1.1"  # From env (different from default)
        assert port == 5000  # From CLI


class TestPrintStartupBanner:
    """Tests for print_startup_banner() function."""
    
    def test_banner_contains_url(self, capsys):
        """
        What it does: Verifies that banner contains the server URL.
        Purpose: Ensure URL is displayed to user.
        """
        print("Setup: Importing print_startup_banner...")
        from main import print_startup_banner
        
        print("Action: Calling print_startup_banner('0.0.0.0', 8000)...")
        print_startup_banner("0.0.0.0", 8000)
        
        captured = capsys.readouterr()
        print(f"Captured output length: {len(captured.out)}")
        
        # When host is 0.0.0.0, display should show localhost
        assert "localhost:8000" in captured.out or "8000" in captured.out
    
    def test_banner_contains_custom_port(self, capsys):
        """
        What it does: Verifies that banner shows custom port.
        Purpose: Ensure custom port is displayed correctly.
        """
        print("Setup: Importing print_startup_banner...")
        from main import print_startup_banner
        
        print("Action: Calling print_startup_banner('127.0.0.1', 9000)...")
        print_startup_banner("127.0.0.1", 9000)
        
        captured = capsys.readouterr()
        print(f"Captured output contains '9000': {'9000' in captured.out}")
        
        assert "9000" in captured.out
    
    def test_banner_contains_docs_url(self, capsys):
        """
        What it does: Verifies that banner contains API docs URL.
        Purpose: Ensure /docs endpoint is mentioned.
        """
        print("Setup: Importing print_startup_banner...")
        from main import print_startup_banner
        
        print("Action: Calling print_startup_banner('0.0.0.0', 8000)...")
        print_startup_banner("0.0.0.0", 8000)
        
        captured = capsys.readouterr()
        print(f"Captured output contains '/docs': {'/docs' in captured.out}")
        
        assert "/docs" in captured.out
    
    def test_banner_contains_health_url(self, capsys):
        """
        What it does: Verifies that banner contains health check URL.
        Purpose: Ensure /health endpoint is mentioned.
        """
        print("Setup: Importing print_startup_banner...")
        from main import print_startup_banner
        
        print("Action: Calling print_startup_banner('0.0.0.0', 8000)...")
        print_startup_banner("0.0.0.0", 8000)
        
        captured = capsys.readouterr()
        print(f"Captured output contains '/health': {'/health' in captured.out}")
        
        assert "/health" in captured.out


class TestCliHelp:
    """Tests for CLI help output."""
    
    def test_help_shows_port_option(self):
        """
        What it does: Verifies that --help shows port option.
        Purpose: Ensure help is informative.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with --help...")
        with patch.object(sys, 'argv', ['main.py', '--help']):
            with pytest.raises(SystemExit) as exc_info:
                parse_cli_args()
        
        print(f"Exit code: {exc_info.value.code}")
        # --help exits with code 0
        assert exc_info.value.code == 0
    
    def test_help_shows_host_option(self, capsys):
        """
        What it does: Verifies that --help output contains host option.
        Purpose: Ensure host option is documented.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with --help...")
        with patch.object(sys, 'argv', ['main.py', '--help']):
            with pytest.raises(SystemExit):
                parse_cli_args()
        
        captured = capsys.readouterr()
        print(f"Help output contains '--host': {'--host' in captured.out}")
        print(f"Help output contains '-H': {'-H' in captured.out}")
        
        assert "--host" in captured.out
        assert "-H" in captured.out


class TestCliVersion:
    """Tests for CLI version output."""
    
    def test_version_flag_exits_with_zero(self):
        """
        What it does: Verifies that --version exits with code 0.
        Purpose: Ensure version flag works correctly.
        """
        print("Setup: Importing parse_cli_args...")
        from main import parse_cli_args
        
        print("Action: Calling parse_cli_args with --version...")
        with patch.object(sys, 'argv', ['main.py', '--version']):
            with pytest.raises(SystemExit) as exc_info:
                parse_cli_args()
        
        print(f"Exit code: {exc_info.value.code}")
        assert exc_info.value.code == 0
    
    def test_version_shows_app_version(self, capsys):
        """
        What it does: Verifies that --version shows application version.
        Purpose: Ensure version is displayed.
        """
        print("Setup: Importing parse_cli_args and APP_VERSION...")
        from main import parse_cli_args
        from kiro.config import APP_VERSION
        
        print("Action: Calling parse_cli_args with --version...")
        with patch.object(sys, 'argv', ['main.py', '--version']):
            with pytest.raises(SystemExit):
                parse_cli_args()
        
        captured = capsys.readouterr()
        print(f"Version output: {captured.out}")
        print(f"APP_VERSION: {APP_VERSION}")
        
        assert APP_VERSION in captured.out