"""
General webinterface client for antMiner
"""

import json
import logging
import requests
import typing
from bos_toolbox.platform.webif import Webif

from collections import OrderedDict
from http import HTTPStatus
from requests.auth import HTTPDigestAuth

LOG = logging.getLogger(__name__)


class AntminerWebif(Webif):
    def __init__(self, ip_address: str, port: int, ssl: bool = False):
        """
        Creates a wrapper around possible HTTP requests of the
        AntMiner webinterface.

        Args:
            ip_address: Ip address of target
            port: Webinterface port
            ssl: Whether to use SSL (default: False)
        """
        Webif.__init__(self, ip_address, port, ssl)

    def ensure_authentication(self, username: str, password: str, **kwargs) -> bool:
        """
        Ensures authentication to webif succeeds

        Args:
            username: Username to authenticate with
            password: Password to authenticate with

        Returns:
            True on success, False otherwise
        """
        self.session.auth = HTTPDigestAuth(username, password)

        resp = self.get(self.base_url, **kwargs)
        return resp and resp.status_code != HTTPStatus.UNAUTHORIZED

    def get_page(self, url_path: str) -> requests.Response:
        """
        Check if a page is available on host

        Args:
            url_path: Path to request

        Returns:
            True if page is available, False otherwise
        """
        url = '{}/{}'.format(self.base_url, url_path)
        return self.get(url)

    def get_index(self) -> requests.Response:
        """
        Simply get the index file

        Returns:
          Response on success, None otherwise
        """
        return self.get(self.base_url)

    def get_system_info(self) -> typing.Union[dict, None]:
        """
        Gets system info of host

        Returns:
            JSON object on success, None otherwise
        """
        url = '{}/cgi-bin/get_system_info.cgi'.format(self.base_url)
        resp = self.get(url)
        if not resp or resp.status_code != HTTPStatus.OK:
            LOG.error('Cannot get system info')
            return None
        if resp.apparent_encoding != 'ascii':
            LOG.error('Unexpected encoding of system info')
            return None
        try:
            result = json.loads(resp.content.decode())
        except json.JSONDecodeError:
            LOG.error('Invalid JSON for system info')
            return None
        except Exception as ex:
            LOG.exception(ex)
            return None

        return result

    def get_network_info(self) -> typing.Union[requests.Response, None]:
        """
        Gets network info via cgi-bin call

        Example response body:
            'ipaddress': '192.168.1.1',
            'macaddr': '0E:3A:E2:0F:46:90',
            'nettype': 'Static',
            'netmask': '255.255.255.0',
            'netdevice': 'eth0',

            'conf_ipaddress': '192.168.1.1',
            'conf_gateway': '',
            'conf_netmask': '255.255.255.0',
            'conf_dnsservers': '',
            'conf_nettype': 'Static',
            'conf_hostname': 'AntMiner'

        Returns:
            Response on success, None otherwise
        """
        url = '{}/cgi-bin/get_network_info.cgi?_={}'.format(
            self.base_url, self._epoch_ms
        )
        return self.get(url)

    def set_network_config(
        self,
        net_type: str,
        hostname: str,
        ip_address: str,
        netmask: str,
        gateway: str,
        dns_servers: str,
        timeout: int,
    ) -> typing.Union[requests.Response, None]:
        """
        Sets network config via cgi-bin call

        IMPORTANT
            Using tuples instead of dict
            AntMiner software is so picky (crappy) - it cannot handle different parameter order

        EXAMPLE DATA
            _ant_conf_nettype"Static"
            _ant_conf_hostname="Ant"
            _ant_conf_ipaddress="192.168.1.1"
            _ant_conf_netmask="255.255.255.0"
            _ant_conf_gateway=""
            _ant_conf_dnsservers=""

        Args:
            net_type: Can be either `Static` or `DHCP`
            hostname: Hostname
            ip_address: Local IP address of host
            netmask: Netmask
            gateway: Gateway
            dns_servers: Dns servers
            timeout: Timeout of request in seconds

        Returns:
            Response on success, None otherwise
        """
        form_data = OrderedDict(
            [
                ('_ant_conf_nettype', net_type),
                ('_ant_conf_hostname', hostname),
                ('_ant_conf_ipaddress', ip_address),
                ('_ant_conf_netmask', netmask),
                ('_ant_conf_gateway', gateway),
                ('_ant_conf_dnsservers', dns_servers),
            ]
        )

        url = '{}/cgi-bin/set_network_conf.cgi'.format(self.base_url)
        return self.post(url, timeout=timeout, data=form_data)

    def create_log_backup(
        self, post_data: str
    ) -> typing.Union[requests.Response, None]:
        """
        Creates log backup on host.

        Returns:
            Response on success, None otherwise
        """
        url = '{}/cgi-bin/create_log_backup.cgi'.format(self.base_url)
        return self.post(url, data=post_data)

    def send_upgrade(
        self,
        data: bytes,
        timeout: int = 10,
        filename: str = 'upgrade.tar.gz',
        clear_settings: bool = False,
    ) -> typing.Union[requests.Response, None]:
        """
        Initiate upgrade via POST request to upgrade.cgi / upgrade_clear.cgi

        Args:
            data: Data to send as upgrade tar.gz
            timeout: Timeout of request in seconds
            filename: Filename to send
            clear_settings: Whether to clean settings after upgrade

        Returns:
            Response on success, None otherwise
        """
        files = {'file': (filename, data, 'application/octet-stream')}
        cgi_script = 'upgrade.cgi' if not clear_settings else 'upgrade_clear.cgi'
        url = '{}/cgi-bin/{}'.format(self.base_url, cgi_script)
        return self.post(url, timeout=timeout, files=files)
