# Copyright (c) 2021 Red Hat, Inc.
#
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
from __future__ import absolute_import

import typing  # noqa
from urllib import parse

import netaddr

import tobiko
from tobiko.shell import sh
from tobiko.shell import ssh


CURL_CONNECTION_ERRORS = {
    7,   # Connection refused
    22,  # 404 Not Found
    28,  # Connection timedout
}


def execute_curl(
        hostname: typing.Union[str, netaddr.IPAddress] = None,
        port: int = None,
        path: str = None,
        scheme: str = None,
        ssh_client: ssh.SSHClientType = None,
        connect_timeout: tobiko.Seconds = None,
        fail_silently: bool = True,
        retry_count: int = None,
        retry_timeout: tobiko.Seconds = None,
        retry_interval: tobiko.Seconds = None,
        **execute_params) -> str:
    """Execute curl command

    Returns the command output in case of success,
    raises ShellCommandFailed otherwise.
    """
    netloc = make_netloc(hostname=hostname, port=port)
    url = make_url(scheme=scheme, netloc=netloc, path=path)
    command = sh.shell_command('curl -g')
    if fail_silently:
        command += '-f'
    if connect_timeout is not None:
        command += f'--connect-timeout {int(connect_timeout)}'
    command += url
    for attempt in tobiko.retry(count=retry_count,
                                timeout=retry_timeout,
                                interval=retry_interval,
                                default_count=1,
                                default_timeout=60.,
                                default_interval=1.):
        try:
            return sh.execute(command, ssh_client=ssh_client,
                              **execute_params).stdout
        except sh.ShellCommandFailed as ex:
            if ex.exit_status in CURL_CONNECTION_ERRORS:
                # Retry on connection errors
                attempt.check_limits()
            else:
                raise
    raise RuntimeError('Unexpected failure')


def make_netloc(
        hostname: typing.Union[str, netaddr.IPAddress, None] = None,
        port: typing.Optional[int] = None,
        username: typing.Optional[str] = None,
        password: typing.Optional[str] = None) -> str:
    if not hostname:
        return ''
    try:
        ip_address = netaddr.IPAddress(hostname)
    except netaddr.AddrFormatError:
        tobiko.check_valid_type(hostname, str)
        netloc = hostname
    else:
        if ip_address.version == 6:
            # Add square brackets around IPv6 address to please curl
            netloc = f'[{hostname}]'
        else:
            netloc = str(hostname)
    if port is not None:
        netloc = f'{netloc}:{port}'
    if username is not None:
        if password is not None:
            netloc = f'{username}:{password}@{netloc}'
        else:
            netloc = f'{username}@{netloc}'
    return netloc


def make_url(scheme: typing.Optional[str] = None,
             netloc: typing.Optional[str] = None,
             path: typing.Optional[str] = None) -> str:
    return parse.SplitResult(scheme=(scheme or 'http').lower(),
                             netloc=netloc or '',
                             path=path or '',
                             query='',
                             fragment='').geturl()
