#!/usr/bin/env python

import os
import click
import dbus
import uuid
import json


@click.group()
def cli():
    pass


@cli.command("backup", help="Backup all wifi connections from NetworkManager.")
@click.argument("filename", required=False)
def backup(filename):
    if not filename:
        filename = "wifi-connections.json"

    cons = list_connections()
    write_to_file(cons, filename)

    print(f"[INFO] Wifi connections backuped to: {filename}")


@cli.command(
    "import", help="Import wifi connections from a file to the NetworkManager."
)
@click.argument("filename", required=False)
def _import(filename):
    if not filename:
        filename = "wifi-connections.json"

    print(f"[INFO] Wifi connections importing from: {filename}")
    if not os.path.isfile(filename):
        print(f"[ERROR] {filename} does not exist")
        return

    with open(filename, "r") as f:
        for json_con in f:
            jcon = json.loads(json_con)

            con = create_wifi_connection_dict(jcon)
            add_connection(con)


def add_connection(connection):
    bus = dbus.SystemBus()
    service_name = "org.freedesktop.NetworkManager"
    proxy = bus.get_object(service_name, "/org/freedesktop/NetworkManager/Settings")
    settings = dbus.Interface(proxy, "org.freedesktop.NetworkManager.Settings")
    settings.AddConnection(connection)


def create_wifi_connection_dict(connection):
    name = connection["name"]
    ssid = connection["ssid"].encode("utf-8")
    secret_type = connection["secret-type"]

    connection_type = dbus.Dictionary(
        {"type": "802-11-wireless", "uuid": str(uuid.uuid4()), "id": name}
    )

    wifi = dbus.Dictionary(
        {
            "ssid": dbus.ByteArray(ssid),
            "mode": "infrastructure",
        }
    )

    wsecurity = dbus.Dictionary({"key-mgmt": "None"})
    if secret_type == "wpa-psk":
        psk = ""
        if "psk" in connection["secrets"]:
            psk = connection["secrets"]["psk"]

        wsecurity = dbus.Dictionary(
            {
                "key-mgmt": "wpa-psk",
                "auth-alg": "open",
                "psk": psk,
            }
        )
    if secret_type == "wpa-eap":
        secrets = connection["secrets"]

        wsecurity = dbus.Dictionary(
            {
                "key-mgmt": "wpa-eap",
                "auth-alg": "open",
            }
        )

        wsecurity_enterprise = dbus.Dictionary(
            {
                "eap": [secrets["eap"]],
                "identity": secrets["identity"],
                "phase2-auth": secrets["phase2-auth"],
                "password": secrets["password"],
            }
        )

        if secrets["anonymous-identity"]:
            wsecurity_enterprise["anonymous-identity"] = secrets["anonymous-identity"]

    ip_settings = dbus.Dictionary({"method": "auto"})
    con = dbus.Dictionary(
        {
            "connection": connection_type,
            "802-11-wireless": wifi,
            "802-11-wireless-security": wsecurity,
            "ipv4": ip_settings,
            "ipv6": ip_settings,
        }
    )

    if secret_type == "wpa-eap":
        con["802-1x"] = wsecurity_enterprise

    return con


def merge_secrets(proxy, config, setting_name):
    try:
        # returns a dict of dicts mapping name::setting,
        # where setting is a dict mapping key::value.
        # Each member of the 'setting' dict is a secret
        secrets = proxy.GetSecrets(setting_name)

        # Copy the secrets into our connection config
        for setting in secrets:
            for key in secrets[setting]:
                config[setting_name][key] = secrets[setting][key]
    except Exception as e:
        pass


def list_connections():
    connections = list()
    bus = dbus.SystemBus()
    service_name = "org.freedesktop.NetworkManager"
    proxy = bus.get_object(service_name, "/org/freedesktop/NetworkManager/Settings")
    settings = dbus.Interface(proxy, "org.freedesktop.NetworkManager.Settings")
    connection_paths = settings.ListConnections()

    for path in connection_paths:
        con_proxy = bus.get_object(service_name, path)
        settings_connection = dbus.Interface(
            con_proxy, "org.freedesktop.NetworkManager.Settings.Connection"
        )
        config = settings_connection.GetSettings()

        merge_secrets(settings_connection, config, "802-11-wireless")
        merge_secrets(settings_connection, config, "802-11-wireless-security")
        merge_secrets(settings_connection, config, "802-1x")

        if config["connection"]["type"] == "802-11-wireless":
            ssid = "".join([chr(byte) for byte in config["802-11-wireless"]["ssid"]])

            sec_type = ""
            sec = {}
            if "802-11-wireless-security" in config:
                sec_type = config["802-11-wireless-security"]["key-mgmt"]
                if sec_type == "wpa-psk":
                    if "psk" in config["802-11-wireless-security"]:
                        sec = {"psk": f"{config['802-11-wireless-security']['psk']}"}
                    else:
                        print(f"[WARN]: Connection {ssid} exported without password")
                if sec_type == "wpa-eap":
                    ano_identity = ""
                    if "anonymous-identity" in config["802-1x"]:
                        ano_identity = f"{config['802-1x']['anonymous-identity']}"

                    sec = {
                        "eap": f"{config['802-1x']['eap'][0]}",
                        "identity": f"{config['802-1x']['identity']}",
                        "anonymous-identity": ano_identity,
                        "phase2-auth": f"{config['802-1x']['phase2-auth']}",
                        "password": f"{config['802-1x']['password']}",
                    }

            con = {
                "name": f"{config['connection']['id']}",
                "ssid": ssid,
                "secret-type": f"{sec_type}",
                "secrets": sec,
            }
            connections.append(con)

    return connections


def write_to_file(connections, filename):
    with open(filename, "w+") as f:
        for con in connections:
            f.write(f"{json.dumps(con)}\n")


if __name__ == "__main__":
    cli()
