from typing import Dict
from typing import Tuple
from typing import List


def extract_relationships(
    schema: Dict,
) -> Dict[
    str,
    Tuple[
        Dict[str, Tuple[str, bool]],
        Dict[str, Tuple[str, bool]],
        List[Tuple[str, bool, Dict]],
    ],
]:
    """this function takes jsonschema and returns a dictionary of it
    where each model (object in the #/definitions) is a key wholes value
    is a tuple of dictionaries of singularly and multiply related models
    and whether they are nullable or not.

    singular means one-to-one or one-to-many, we dont know yet
    multiple means many-to-one or many-to-many, we dont know yet

    example argument:
    >>> {
    >>>     "properties": {
    >>>         "Patient": {
    >>>             "properties": {
    >>>                 "doctor": {"$ref": "#/definitions/Doctor"},
    >>>                 "address": {"items": {"$ref": "#/definitions/Address"}},
    >>>             }
    >>>         },
    >>>         "Doctor": {"properties": {}},
    >>>         "Address": {"properties": {"patient": {"$ref": "#/definitions/Patient"}}},
    >>>     }
    >>> }


    example result
    >>> {
    >>>     'Patient': (
    >>>         {'Doctor': ('doctor', True)},
    >>>         {'Address': ('address', True)},
    >>>         []
    >>>     ),
    >>>     'Doctor': ({}, {}, []),
    >>>     'Address': (
    >>>         {'Patient': ('patient', True)},
    >>>         {},
    >>>         []
    >>>     )
    >>> }
    """
    relationships = {}
    definitions = schema.get("definitions", {})
    properties = schema.get("properties", {})

    for model_name, model in properties.items():
        if "$ref" in model:
            ref = model.pop("$ref").split("/")[-1]
            model.update(definitions.get(ref, {}))

        required = model.get("required", [])
        single, many, json = {}, {}, []

        if "properties" in model:  # then this is some kind of object

            for name, properties in model.get("properties", {}).items():
                # loop through all fields

                # is this field nullable?
                # if null is one of the types then yes
                # or if null is not required then also yes
                null = "null" in properties.get("type", []) or name not in required

                # is this field a direct reference to another object?
                if properties.get("$ref"):
                    # ok what is the name of the related object
                    ref = properties.get("$ref").split("/")[-1]
                    # is this related object in the root properties?
                    if ref in schema["properties"]:
                        # if so then its a model, lets record that
                        single[ref] = name, null
                    else:
                        json.append((name, null, properties))

                # could this field an array of references to another object?
                elif properties.get("items"):
                    # etc...
                    if properties.get("items").get("$ref"):
                        ref = properties.get("items").get("$ref").split("/")[-1]
                        if ref in schema["properties"]:
                            many[ref] = name, null
                        else:
                            json.append((name, null, properties))

        relationships[model_name] = single, many, json

    return relationships