# Copyright (c) 2022, Thomas Goirand <zigo@debian.org>
#           (c) 2022, Axel Jacquet <axel.jacquet@infomaniak.com>
#
# 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.

import idna
import requests

from designateclient import client as _designateclient
from oslo_config import cfg
from oslo_log import log as logging
from openstack import connection
from keystoneauth1 import loading as ks_loading


common_opts = [
    cfg.StrOpt('psl_url',
                default='https://publicsuffix.org/list/public_suffix_list.dat',
                help='Mozilla url of official TLDs list'),
    cfg.BoolOpt('http_proxy',
                default=False,
                help='Active http_proxy connection to pull TLDs list'),
    cfg.StrOpt('http_proxy_url',
                default='http://example.com:3128',
                help='http proxy url'),
    cfg.BoolOpt('dry_run',
                default=True,
                help='Displays the list of added and deleted Tlds against the current Designate service list.'),
    cfg.BoolOpt('debug',
                short='d',
                default=False,
                help='If set to true, the logging level will be set to '
                     'DEBUG instead of the default INFO level.'),
]


def list_opts():
    return [
        ('DEFAULT', common_opts),
    ]

def connect_to_designate(CONF, ks_loading):
    ks_loading.register_auth_conf_options(CONF, group='keystone_authtoken')
    ks_loading.register_session_conf_options(CONF, group='keystone_authtoken')
    auth = ks_loading.load_auth_from_conf_options(CONF, 'keystone_authtoken')
    keystone_session = ks_loading.load_session_from_conf_options(CONF, 'keystone_authtoken', auth=auth)
    _DESIGNATE_CONNECTION = connection.Connection(
            session=keystone_session,
            CONF=CONF)
    return _DESIGNATE_CONNECTION

def designate_client(designate_connection):
    return _designateclient.Client("2", session=designate_connection.session)

def pull_tlds():
    """Pull tlds from https://publicsuffix.org/list/public_suffix_list.dat Mozilla official list
    :param none
    list: list of all tlds
    """
    if not CONF.http_proxy:
        print(f"Fetching {CONF.psl_url} without proxy.")
        r = requests.get(CONF.psl_url)

    else:
        proxies = {
           'http': CONF.http_proxy_url,
           'https': CONF.http_proxy_url,
        }
        r = requests.get(CONF.psl_url, proxies=proxies)
        print(f"Fetching {CONF.psl_url} with proxy.")
        print(f"with proxy server {r.status_code=}")

    if r.status_code != requests.codes.ok or len(r.content) == 0:
        raise Exception("Could not download PSL from " + CONF.psl_url)
    tlds_raw = r.text

    tlds_list = []

    for line in tlds_raw.split("\n"):
        if not line.strip() or line[:2] == "//" or line[:1] in ("*", "!"):
            continue
        line = idna.encode(line).decode()
        tlds_list.append(line)

    return tlds_list

def compare_tlds(dns):
    """compare tlds Designate with official tlds list and update Designate if diffenrence
    :param none
    :return added,deleted TLDs list
    """
    tlds_official = pull_tlds()
    tlds = dns.tlds.list(limit=100000000)
    tlds_designate = [tld["name"] for tld in tlds]

    new_set = set(tlds_official)
    old_set = set(tlds_designate)
    return new_set - old_set, old_set - new_set

def list_update_tlds(dns):
    """Displays the list of added and deleted Tlds against the current Designate service list
    :param designate connection
    :return none
    """
    added, deleted = compare_tlds(dns)
    print({"added": list(added), "deleted": list(deleted)})

def update_tlds(dns):
    """Update tlds Designate with official tlds list
    :param designate connection
    :return none
    """
    added, deleted = compare_tlds(dns)

    if added:
        for tld in added:
            print(f"Adding {tld}")
            dns.tlds.create(tld)

    if deleted:
        for tld in deleted:
            print(f"Deleting {tld}")
            dns.tlds.delete(tld)


def run_tlds():
    global CONF
    global LOG

    LOG = logging.getLogger(__name__)

    CONF = cfg.ConfigOpts()
    CONF.register_cli_opts(common_opts)
    CONF(project='designate-tlds')

    logging.set_defaults(
          default_log_levels=logging.get_default_log_levels())

    designate_connection = connect_to_designate(CONF, ks_loading)
    pull_tlds()
    dns = designate_client(designate_connection)

    if CONF.dry_run:
        list_update_tlds(dns)
    else:
        update_tlds(dns)
