import ipaddress
import socket
import textwrap
import urllib

import pytest

import dynv6_update_zone


def test_host_addresses(monkeypatch):
    ipv6 = "2001:db8::"

    def _getaddrinfo(*args, **kwargs):
        return [
            (socket.AF_INET6, socket.SOCK_STREAM, 0, "", (ipv6, 0, 0, 0)),
            (socket.AF_INET, socket.SOCK_STREAM, 6, "", ("192.0.2.1", 0)),
        ]

    monkeypatch.setattr("socket.getaddrinfo", _getaddrinfo)
    assert dynv6_update_zone.host_addresses("a.b.c") == {ipaddress.IPv6Address(ipv6)}


def test_host_addresses_error(monkeypatch):
    exc = Exception("uh oh!")

    def _getaddrinfo(*args, **kwargs):
        raise exc

    monkeypatch.setattr("socket.getaddrinfo", _getaddrinfo)
    with pytest.raises(dynv6_update_zone.HostnameResolutionError) as exc_info:
        dynv6_update_zone.host_addresses("a.b.c")
    assert exc_info.value.__cause__ is exc


def test_local_addresses(monkeypatch):
    def _check_output(*args, **kwargs):
        return textwrap.dedent(
            """
            2: wlp0s20f3: <BROADCAST,MULTICAST,UP,LOWER_UP>
             mtu 1500 qdisc noqueue state UP group default qlen 1000
                inet6 2001:db8::1/64 scope global temporary dynamic
                   valid_lft 86149sec preferred_lft 82736sec
                inet6 2001:db8::2/64 scope global dynamic mngtmpaddr noprefixroute
                   valid_lft 86149sec preferred_lft 86149sec
            """
        ).encode()

    monkeypatch.setattr("subprocess.check_output", _check_output)
    assert dynv6_update_zone.local_addresses(network="2001:db8::/64") == {
        ipaddress.IPv6Address("2001:db8::1"),
        ipaddress.IPv6Address("2001:db8::2"),
    }


def test_local_addresses_error(monkeypatch):
    exc = Exception("uh oh!")

    def _check_output(*args, **kwargs):
        raise exc

    monkeypatch.setattr("subprocess.check_output", _check_output)
    with pytest.raises(dynv6_update_zone.AddressRetrievalError) as exc_info:
        dynv6_update_zone.local_addresses(network="2001:db8::/64")
    assert exc_info.value.__cause__ is exc


def test_unordered_list():
    assert (
        dynv6_update_zone.unordered_list(["a", "b", "c"], prefix="* ")
        == "* a\n* b\n* c"
    )


def test_update_gaierror(monkeypatch, caplog):
    monkeypatch.setenv("token", "abc123")
    err = -2
    msg = "Name or service not known"

    def _urlopen(*args, **kwargs):
        raise urllib.error.URLError(socket.gaierror(err, msg))

    monkeypatch.setattr("urllib.request.urlopen", _urlopen)
    with pytest.raises(dynv6_update_zone.ZoneUpdateError):
        dynv6_update_zone.update_zone("a.b.c")
    [record] = caplog.records
    assert record.levelname == "ERROR"
    assert record.msg == f"{dynv6_update_zone.UPDATE_URL.netloc}: [Errno {err}] {msg}"


def test_update_502(monkeypatch, caplog):
    monkeypatch.setenv("token", "abc123")
    monkeypatch.setattr(
        "dynv6_update_zone.UPDATE_URL",
        urllib.parse.urlsplit("https://httpbin.org/status/502"),
    )
    with pytest.raises(dynv6_update_zone.ZoneUpdateError):
        dynv6_update_zone.update_zone("a.b.c")
    [record] = caplog.records
    assert record.levelname == "ERROR"
    assert (
        record.msg
        == f"{dynv6_update_zone.UPDATE_URL.netloc}: HTTP Error 502: BAD GATEWAY"
    )