# -*- coding: utf-8 -*-

""" License

    Copyright (C) 2018 YUNOHOST.ORG

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program; if not, see http://www.gnu.org/licenses

"""
import dns.resolver
from publicsuffixlist import PublicSuffixList
from moulinette.utils.filesystem import read_file

YNH_DYNDNS_DOMAINS = ["nohost.me", "noho.st", "ynh.fr"]

# Lazy dev caching to avoid re-reading the file multiple time when calling
# dig() often during same yunohost operation
external_resolvers_ = []


def external_resolvers():

    global external_resolvers_

    if not external_resolvers_:
        resolv_dnsmasq_conf = read_file("/etc/resolv.dnsmasq.conf").split("\n")
        external_resolvers_ = [
            r.split(" ")[1] for r in resolv_dnsmasq_conf if r.startswith("nameserver")
        ]
        # We keep only ipv4 resolvers, otherwise on IPv4-only instances, IPv6
        # will be tried anyway resulting in super-slow dig requests that'll wait
        # until timeout...
        external_resolvers_ = [r for r in external_resolvers_ if ":" not in r]

    return external_resolvers_


def dig(
    qname, rdtype="A", timeout=5, resolvers="local", edns_size=1500, full_answers=False
):
    """
    Do a quick DNS request and avoid the "search" trap inside /etc/resolv.conf
    """

    # It's very important to do the request with a qname ended by .
    # If we don't and the domain fail, dns resolver try a second request
    # by concatenate the qname with the end of the "hostname"
    if not qname.endswith("."):
        qname += "."

    if resolvers == "local":
        resolvers = ["127.0.0.1"]
    elif resolvers == "force_external":
        resolvers = external_resolvers()
    else:
        assert isinstance(resolvers, list)

    resolver = dns.resolver.Resolver(configure=False)
    resolver.use_edns(0, 0, edns_size)
    resolver.nameservers = resolvers
    # resolver.timeout is used to trigger the next DNS query on resolvers list.
    # In python-dns 1.16, this value is set to 2.0. However, this means that if
    # the 3 first dns resolvers in list are down, we wait 6 seconds before to
    # run the DNS query to a DNS resolvers up...
    # In diagnosis dnsrecords, with 10 domains this means at least 12min, too long.
    resolver.timeout = 1.0
    # resolver.lifetime is the timeout for resolver.query()
    # By default set it to 5 seconds to allow 4 resolvers to be unreachable.
    resolver.lifetime = timeout
    try:
        answers = resolver.query(qname, rdtype)
    except (
        dns.resolver.NXDOMAIN,
        dns.resolver.NoNameservers,
        dns.resolver.NoAnswer,
        dns.exception.Timeout,
    ) as e:
        return ("nok", (e.__class__.__name__, e))

    if not full_answers:
        answers = [answer.to_text() for answer in answers]

    return ("ok", answers)


def get_public_suffix(domain):
    """get_public_suffix("www.example.com") -> "example.com"

    Return the public suffix of a domain name based
    """
    # Load domain public suffixes
    psl = PublicSuffixList()

    public_suffix = psl.publicsuffix(domain)

    # FIXME: wtf is this supposed to do ? :|
    if public_suffix in YNH_DYNDNS_DOMAINS:
        domain_prefix = domain[0:-(1 + len(public_suffix))]
        public_suffix = domain_prefix.split(".")[-1] + "." + public_suffix

    return public_suffix


def get_dns_zone_from_domain(domain):
    # TODO Check if this function is YNH_DYNDNS_DOMAINS compatible
    """
    Get the DNS zone of a domain

    Keyword arguments:
        domain -- The domain name

    """

    # For foo.bar.baz.gni we want to scan all the parent domains
    # (including the domain itself)
    # foo.bar.baz.gni
    #     bar.baz.gni
    #         baz.gni
    #             gni
    parent_list = [domain.split(".", i)[-1]
                   for i, _ in enumerate(domain.split("."))]

    for parent in parent_list:

        # Check if there's a NS record for that domain
        answer = dig(parent, rdtype="NS", full_answers=True, resolvers="force_external")
        if answer[0] == "ok":
            # Domain is dns_zone
            return parent
        # Otherwise, check if the parent of this parent is in the public suffix list
        if parent.split(".", 1)[-1] == get_public_suffix(parent):
            # Couldn't check if domain is dns zone,    # FIXME : why "couldn't" ...?
            # returning private suffix
            return parent

    # FIXME: returning None will probably trigger bugs when this happens, code expects a domain string
    return None

@@ -770,155 +770,52 @@
 rom 
-publicsuffixlist import PublicSuffixList%0Afrom moulinette.utils.filesystem import read_file%0A%0AYNH_DYNDNS_DOMAINS = %5B%22nohost.me%22, %22noho.st%22, %22ynh.fr%22%5D
+moulinette.utils.filesystem import read_file
 %0A%0A# 
@@ -3286,523 +3286,8 @@
 )%0A%0A%0A
-def get_public_suffix(domain):%0A    %22%22%22get_public_suffix(%22www.example.com%22) -%3E %22example.com%22%0A%0A    Return the public suffix of a domain name based%0A    %22%22%22%0A    # Load domain public suffixes%0A    psl = PublicSuffixList()%0A%0A    public_suffix = psl.publicsuffix(domain)%0A%0A    # FIXME: wtf is this supposed to do ? :%7C%0A    if public_suffix in YNH_DYNDNS_DOMAINS:%0A        domain_prefix = domain%5B0:-(1 + len(public_suffix))%5D%0A        public_suffix = domain_prefix.split(%22.%22)%5B-1%5D + %22.%22 + public_suffix%0A%0A    return public_suffix%0A%0A%0A
 def 
@@ -4059,309 +4059,8 @@
 rent
-%0A        # Otherwise, check if the parent of this parent is in the public suffix list%0A        if parent.split(%22.%22, 1)%5B-1%5D == get_public_suffix(parent):%0A            # Couldn't check if domain is dns zone,    # FIXME : why %22couldn't%22 ...?%0A            # returning private suffix%0A            return parent
 %0A%0A  

remove dependency to public suffix list, not sure what scenario it was supposed to cover, probably goodenough without it