"""Test the server's shell(reader, writer) callback."""

# std imports
import time
import asyncio

# 3rd party
import pytest

# local
from telnetlib3.client import _transform_args, _get_argument_parser
from telnetlib3.telopt import DO, IAC, WONT, TTYPE
from telnetlib3.tests.accessories import create_server, open_connection, asyncio_connection


async def test_telnet_server_default_timeout(bind_host, unused_tcp_port):
    """Test callback on_timeout() as coroutine of create_server()."""
    given_timeout = 19.29

    async with create_server(host=bind_host, port=unused_tcp_port, timeout=given_timeout) as server:
        async with asyncio_connection(bind_host, unused_tcp_port) as (reader, writer):
            writer.write(IAC + WONT + TTYPE)

            srv_instance = await asyncio.wait_for(server.wait_for_client(), 0.5)
            assert srv_instance.get_extra_info("timeout") == given_timeout

            srv_instance.set_timeout()
            assert srv_instance.get_extra_info("timeout") == given_timeout


async def test_telnet_server_set_timeout(bind_host, unused_tcp_port):
    """Test callback on_timeout() as coroutine of create_server()."""
    async with create_server(host=bind_host, port=unused_tcp_port) as server:
        async with asyncio_connection(bind_host, unused_tcp_port) as (reader, writer):
            writer.write(IAC + WONT + TTYPE)

            srv_instance = await asyncio.wait_for(server.wait_for_client(), 0.5)
            for value in (19.29, 0):
                srv_instance.set_timeout(value)
                assert srv_instance.get_extra_info("timeout") == value

            srv_instance.set_timeout()
            assert srv_instance.get_extra_info("timeout") == 0


@pytest.mark.parametrize(
    "timeout,encoding,elapsed_min,elapsed_max",
    [(0.050, "utf-8", 0.035, 0.150), (0.150, False, 0.050, 0.200)],
)
async def test_telnet_server_waitfor_timeout(
    bind_host, unused_tcp_port, timeout, encoding, elapsed_min, elapsed_max
):
    """Test callback on_timeout() as coroutine of create_server()."""
    expected_output = IAC + DO + TTYPE + b"\r\nTimeout.\r\n"

    async with create_server(
        host=bind_host, port=unused_tcp_port, timeout=timeout, encoding=encoding
    ):
        async with asyncio_connection(bind_host, unused_tcp_port) as (reader, writer):
            writer.write(IAC + WONT + TTYPE)

            stime = time.time()
            output = await asyncio.wait_for(reader.read(), 0.5)
            elapsed = time.time() - stime
            assert elapsed_min <= round(elapsed, 3) <= elapsed_max
            assert output == expected_output


async def test_open_connection_connect_timeout(bind_host, unused_tcp_port):
    """Test connect_timeout raises ConnectionError on unreachable port."""
    with pytest.raises(ConnectionError):
        async with open_connection(bind_host, unused_tcp_port, connect_timeout=0.1, encoding=False):
            pass


async def test_open_connection_connect_timeout_success(bind_host, unused_tcp_port):
    """Test connect_timeout does not interfere with successful connection."""
    async with create_server(host=bind_host, port=unused_tcp_port):
        async with open_connection(
            bind_host, unused_tcp_port, connect_timeout=5.0, encoding=False, connect_maxwait=0.5
        ):
            pass


def test_cli_connect_timeout_arg():
    """Test --connect-timeout CLI argument is parsed."""
    parser = _get_argument_parser()
    args = parser.parse_args(["example.com", "--connect-timeout", "2.5"])
    assert _transform_args(args)["connect_timeout"] == 2.5


def test_cli_connect_timeout_default():
    """Test --connect-timeout defaults to 10."""
    parser = _get_argument_parser()
    assert _transform_args(parser.parse_args(["example.com"]))["connect_timeout"] == 10
