"""
Common Output Format for passive DNS library.

"""

import ipaddress
import sys
import ndjson


def is_valid_ip(ip: str) -> bool:
    """Check if an IP address given as string would be convertible to
    an ipaddress object (and thus if it is a valid IP).

    Returns
    --------
    True on success, False on validation failure.
    """

    try:
        ipaddress.ip_address(ip)
    except Exception as ex:
        print("is_valid_ip(%s) returned False. Reason: %s" % (ip, str(ex)), file=sys.stderr)
        return False
    return True


def is_cof_valid_simple(d: dict) -> bool:
    """Check MANDATORY fields according to COF - simple check, do not do the full JSON schema validation.

    Returns
    --------
    True on success, False on validation failure.
    """

    if "rrname" not in d:
        print("Missing MANDATORY field 'rrname'", file=sys.stderr)
        return False
    if not isinstance(d['rrname'], str):
        print("Type error: 'rrname' is not a JSON string", file=sys.stderr)
        return False
    if "rrtype" not in d:
        print("Missing MANDATORY field 'rrtype'", file=sys.stderr)
        return False
    if not isinstance(d['rrtype'], str):
        print("Type error: 'rrtype' is not a JSON string", file=sys.stderr)
        return False
    if "rdata" not in d:
        print("Missing MANDATORY field 'rdata'", file=sys.stderr)
        return False
    if "rdata" not in d:
        print("Missing MANDATORY field 'rdata'", file=sys.stderr)
        return False
    if not isinstance(d['rdata'], str) and not isinstance(d['rdata'], list):
        print("'rdata' is not a list and not a string.", file=sys.stderr)
        return False
    if not ("time_first" in d and "time_last" in d) or ("zone_time_first" in d and "zone_time_last" in d):
        print("We are missing EITHER ('first_seen' and 'last_seen') OR ('zone_time_first' and zone_time_last') fields", file=sys.stderr)
        return False
    # currently we don't check the OPTIONAL fields. Sorry... to be done later.
    return True


def validate_cof(d: dict, strict=False) -> bool:
    """Validate an input passive DNS COF (given as dict).
    strict might be set to False in order to loosen the checking.
    With strict==True, a full JSON Schema validation will happen.


    Returns
    --------
    True on success, False on validation failure.
    """
    if not strict:
        return is_cof_valid_simple(d)


if __name__ == "__main__":
    # simple, poor man's unit tests.

    print(80*"=", file=sys.stderr)
    print("Unit Tests:", file=sys.stderr)
    assert not is_valid_ip("a.2.3.4")
    assert is_valid_ip("99.88.77.6")
    assert is_valid_ip("2a0c:88:77:6::1")

    # COF validation
    mock_input = """{"count":1909,"rdata":["cpa.circl.lu"],"rrname":"www.circl.lu","rrtype":"CNAME","time_first":"1315586409","time_last":"1449566799"}
{"count":2560,"rdata":["cpab.circl.lu"],"rrname":"www.circl.lu","rrtype":"CNAME","time_first":"1449584660","time_last":"1617676151"}"""

    i = 0
    for entry in ndjson.loads(mock_input):
        retval = validate_cof(entry, strict=False)
        assert retval
        print("line %d is valid: %s" % (i, retval))
        i += 1

    print(80*"=", file=sys.stderr)
    print("Unit Tests DONE", file=sys.stderr)

@@ -536,24 +536,239 @@
 turn True%0A%0A%0A
+def is_cof_valid_strict(d: dict) -%3E bool:%0A    %22%22%22Check the COF - do the full JSON schema validation.%0A%0A    Returns%0A    --------%0A    True on success, False on validation failure.%0A    %22%22%22%0A    return True     # FIXME%0A%0A%0A
 def is_cof_v
@@ -789,32 +789,32 @@
  dict) -%3E bool:%0A
-
     %22%22%22Check MAN
@@ -2283,20 +2283,19 @@
  strict=
-Fals
+Tru
 e) -%3E bo
@@ -2630,16 +2630,64 @@
 mple(d)%0A
+    else:%0A        return is_cof_valid_strict(d)%0A
 %0A%0Aif __n
@@ -3428,16 +3428,16 @@
 etval))%0A
-
         
@@ -3444,16 +3444,319 @@
 i += 1%0A%0A
+    test2 = '%7B%22count%22: 2, %22time_first%22: 1619556027, %22time_last%22: 1619556034, %22rrname%22: %22westernunion.com.ph.unblock-all.com.beta.opera-mini.net.%22, %22rrtype%22: %22A%22, %22bailiwick%22: %22beta.opera-mini.net.%22, %22rdata%22: %5B%22185.26.181.253%22%5D%7D'%0A    for entry in ndjson.loads(test2):%0A        assert validate_cof(entry)%0A%0A
     prin

Make stub strict parser