import random
import socket
import ssl
from typing import Union, List

import aiohttp


def is_host_listening(
    address: str, port: int, timeout: float = 2.0, family=socket.AF_INET, typ=socket.SOCK_STREAM
) -> bool:
    s = socket.socket(family, typ)
    s.settimeout(timeout)
    try:
        err = s.connect_ex((address, port))
        return err == 0
    except socket.error:
        return False
    finally:
        s.close()


def get_free_ports_in_range(
    address: str = "127.0.0.1",
    min_port: int = 49152,
    max_port: int = 65536,
    timeout: float = 0.1,
    family=socket.AF_INET,
    typ=socket.SOCK_STREAM,
    return_after_n_found: Union[None, int] = None,
) -> List[int]:
    free_ports = []
    ports = list(range(min_port, max_port))
    random.shuffle(ports)
    for port in ports:
        if not is_host_listening(address, port, timeout, family, typ):
            free_ports.append(port)
        if return_after_n_found is not None and len(free_ports) >= return_after_n_found:
            break
    return free_ports


def get_free_ports_for_dynamic_service(number) -> List[int]:
    free_ports = get_free_ports_in_range(
        address="127.0.0.1", min_port=14020, max_port=18020, timeout=0.01, return_after_n_found=number
    )
    if len(free_ports) != number:
        raise Exception("Failed to find free port(s) for dynamic service")
    return free_ports


def get_free_port_for_dynamic_service() -> int:
    return get_free_ports_for_dynamic_service(1)[0]


def create_session(cafile=None, cadata=None, **kwargs) -> aiohttp.ClientSession:
    """
    Custom CA setup for sessions.
    """
    ssl_ctx = ssl.create_default_context(cafile=cafile, cadata=cadata)
    conn = aiohttp.TCPConnector(ssl=ssl_ctx)
    return aiohttp.ClientSession(connector=conn, **kwargs)
