import re
from typing import List, Tuple

from ._exception import ConnectionActivateFailedException
from ._helper import add_fields_option_if_needed, add_wait_option_if_needed
from ._system import SystemCommand, SystemCommandInterface
from .data.device import Device, DeviceDetails, DeviceWifi
from .data.hotspot import Hotspot


def parse_wifi_hotspot_result(text: str) -> Tuple[str, str, str]:
    m = re.search(r'Hotspot\spassword:\s(.*)', text)
    if m:
        password = m.groups()[0]
    m = re.search(
        r"Device\s'(.*)'\ssuccessfully\sactivated\swith\s'(.*)'", text)
    if m:
        ifname, uuid = m.groups()
    return (uuid, ifname, password)


def parse_hotspot_properties(text: str) -> Tuple[str, str]:
    for row in text.split('\n'):
        if len(row) == 0:
            continue
        m = re.search(r'^([\w\.\-]+):([\S\s]+)', row)
        if m is None:
            continue
        key, value = m.groups()
        if key == 'connection.id':
            con_name = value
        elif key == '802-11-wireless.ssid':
            ssid = value
    return (con_name, ssid)


class DeviceControlInterface:

    def __call__(self) -> List[Device]:
        raise NotImplementedError

    def status(self) -> List[Device]:
        raise NotImplementedError

    def show(self, ifname: str, fields: str = None) -> DeviceDetails:
        raise NotImplementedError

    def show_all(self, fields: str = None) -> List[DeviceDetails]:
        raise NotImplementedError

    def connect(self, ifname: str, wait: int = None) -> None:
        raise NotImplementedError

    def disconnect(self, ifname: str, wait: int = None) -> None:
        raise NotImplementedError

    def reapply(self, ifname: str) -> None:
        raise NotImplementedError

    def delete(self, ifname: str, wait: int = None) -> None:
        raise NotImplementedError

    def wifi(self, ifname: str = None) -> List[DeviceWifi]:
        raise NotImplementedError

    def wifi_connect(self,
                     ssid: str,
                     password: str,
                     ifname: str = None,
                     wait: int = None) -> None:
        raise NotImplementedError

    def wifi_hotspot(self,
                     ifname: str = None,
                     con_name: str = None,
                     ssid: str = None,
                     band: str = None,
                     channel: int = None,
                     password: str = None) -> Hotspot:
        raise NotImplementedError

    def wifi_rescan(self,
                    ifname: str = None,
                    ssid: str = None) -> None:
        raise NotImplementedError


class DeviceControl(DeviceControlInterface):

    def __init__(self, syscmd: SystemCommandInterface = None):
        self._syscmd = syscmd or SystemCommand()

    def __call__(self) -> List[Device]:
        return self.status()

    def status(self) -> List[Device]:
        r = self._syscmd.nmcli(['device', 'status'])
        results = []
        for row in r.split('\n')[1:]:
            if len(row) > 0:
                results.append(Device.parse(row))
        return results

    def show(self, ifname: str, fields: str = None) -> DeviceDetails:
        cmd = add_fields_option_if_needed(fields) + ['device', 'show', ifname]
        r = self._syscmd.nmcli(cmd)
        results = {}
        for row in r.split('\n'):
            m = re.search(r'^(\S+):\s*([\S\s]+)\s*', row)
            if m:
                key, value = m.groups()
                results[key] = None if value in ('--', '""') else value
        return results

    def show_all(self, fields: str = None) -> List[DeviceDetails]:
        cmd = add_fields_option_if_needed(fields) + ['device', 'show']
        r = self._syscmd.nmcli(cmd)
        results = []
        details: DeviceDetails = {}
        for row in r.split('\n'):
            m = re.search(r'^(\S+):\s*([\S\s]+)\s*', row)
            if m:
                key, value = m.groups()
                if key == 'GENERAL.DEVICE':
                    details = {}
                    results.append(details)
                details[key] = None if value in ('--', '""') else value
        return results

    def connect(self, ifname: str, wait: int = None) -> None:
        cmd = add_wait_option_if_needed(
            wait) + ['device', 'connect', ifname]
        self._syscmd.nmcli(cmd)

    def disconnect(self, ifname: str, wait: int = None) -> None:
        cmd = add_wait_option_if_needed(
            wait) + ['device', 'disconnect', ifname]
        self._syscmd.nmcli(cmd)

    def reapply(self, ifname: str) -> None:
        self._syscmd.nmcli(['device', 'reapply', ifname])

    def delete(self, ifname: str, wait: int = None) -> None:
        cmd = add_wait_option_if_needed(
            wait) + ['device', 'delete', ifname]
        self._syscmd.nmcli(cmd)

    def wifi(self, ifname: str = None) -> List[DeviceWifi]:
        cmd = ['-t', '-f', 'IN-USE,SSID,BSSID,MODE,CHAN,FREQ,RATE,SIGNAL,SECURITY',
               'device', 'wifi', 'list']
        if ifname is not None:
            cmd += ['ifname', ifname]
        r = self._syscmd.nmcli(cmd)
        results = []
        rows = r.split('\n')
        for row in rows:
            if len(row) > 0:
                results.append(DeviceWifi.parse(row))
        return results

    def wifi_connect(self,
                     ssid: str,
                     password: str,
                     ifname: str = None,
                     wait: int = None) -> None:
        cmd = add_wait_option_if_needed(
            wait) + ['device', 'wifi', 'connect', ssid, 'password', password]
        if ifname is not None:
            cmd += ['ifname', ifname]
        r = self._syscmd.nmcli(cmd)
        m = re.search(r'Connection activation failed:', r)
        if m:
            raise ConnectionActivateFailedException(
                'Connection activation failed')

    def wifi_hotspot(self,
                     ifname: str = None,
                     con_name: str = None,
                     ssid: str = None,
                     band: str = None,
                     channel: int = None,
                     password: str = None) -> Hotspot:
        cmd = ['device', 'wifi', 'hotspot', '--show-secrets']
        if ifname is not None:
            cmd += ['ifname', ifname]
        if con_name is not None:
            cmd += ['con-name', con_name]
        if ssid is not None:
            cmd += ['ssid', ssid]
        if band is not None:
            cmd += ['band', band]
        if channel is not None:
            cmd += ['channel', str(channel)]
        if password is not None:
            cmd += ['password', password]
        r = self._syscmd.nmcli(cmd)
        uuid, ifname, password = parse_wifi_hotspot_result(r)
        r = self._syscmd.nmcli(['-t', '-f', 'connection.id,802-11-wireless.ssid',
                                'connection', 'show', 'uuid', uuid])
        con_name, ssid = parse_hotspot_properties(r)
        return Hotspot(ifname, con_name, ssid, password)

    def wifi_rescan(self,
                    ifname: str = None,
                    ssid: str = None) -> None:
        cmd = ['device', 'wifi', 'rescan']
        if ifname is not None:
            cmd += ['ifname', ifname]
        if ssid is not None:
            cmd += ['ssid', ssid]
        self._syscmd.nmcli(cmd)
