from .filesystem import write_graph
from pathlib import Path
from rdflib import Literal, RDF, URIRef, Namespace, Graph
from rdflib.namespace import DC, RDFS, OWL, FOAF, XSD, SKOS
import datetime


class CommonNamespaces:
    def __init__(self):
        self.nb = Namespace("http://ontology.bonsai.uno/core#")
        self.owltime = Namespace("https://www.w3.org/TR/owl-time/")
        self.vann = Namespace("http://purl.org/vocab/vann/")
        self.dt = Namespace("http://purl.org/dc/dcmitype/")


NS = CommonNamespaces()


def add_common_elements(graph, base_uri, title, description, author, version):
    """Add common graphs binds (abbreviations for longer namespaces) and a ``Dataset`` element.

    Input arguments:

    * ``graph``: A ``rdflib.Graph`` object
    * ``base_uri``: A string URI. Must end with ``/``.
    * ``title``, ``description``, ``author``, ``version``: Strings describing the relevant properties.

    Returns the modified graph.

    """
    if not base_uri.endswith("/"):
        raise ValueError("`base_uri` must end with '/'")

    graph.bind('bont', 'http://ontology.bonsai.uno/core#')
    graph.bind("dc", DC)
    graph.bind("foaf", FOAF)
    graph.bind("xsd", XSD)
    graph.bind("owl", OWL)
    graph.bind('skos', SKOS)
    graph.bind("ot", "https://www.w3.org/TR/owl-time/")
    graph.bind("dtype", "http://purl.org/dc/dcmitype/")

    node = URIRef(base_uri)
    graph.add((node, RDF.type, NS.dt.Dataset))
    graph.add((node, DC.title, Literal(title)))
    graph.add((node, DC.description, Literal(description)))
    graph.add((node, FOAF.homepage, URIRef(base_uri + "documentation.html")))
    graph.add((node, NS.vann.preferredNamespaceUri, URIRef(base_uri + "#")))
    graph.add((node, OWL.versionInfo, Literal(version)))
    today = datetime.datetime.now().strftime("%Y-%m-%d")
    graph.add((node, DC.modified,
               Literal(today, datatype=XSD.date)))
    graph.add((node, DC.publisher, Literal("bonsai.uno")))
    graph.add((node, DC.creator, URIRef("http://bonsai.uno/foaf/bonsai.rdf#bonsai")))
    graph.add((node, DC.contributor, Literal(author)))
    graph.add((node, URIRef("http://creativecommons.org/ns#license"), URIRef('http://creativecommons.org/licenses/by/3.0/')))

    return graph


def generate_generic_graph(output_base_dir, kind, data, directory_structure,
                           title, description, author, version,
                           custom_binds=None):
    """Generate a complete ``Turtle`` file describing a specific set of graph metadata.

    Input args:

    * ``output_base_dir``: String or ``Path``. Starting output directory where subdirectories and files will be created.
    * ``kind``: String. The type of objects to be created. See below for details.
    * ``data``: A list of ``("label", "uri_suffix", "")``. See notes below.
    * ``directory_structure``: A list of subdirectory names. If ``kind`` is a string, shouldn't contain the ``kind``. E.g. ``['lcia', 'climate_change']``.
    * ``title``: String.
    * ``description``: String.
    * ``author``: String.
    * ``version``: String.
    * ``custom_binds``: TODO

     **``kind``**

    The valid BONSAI string types are:

    * ``ActivityType``
    * ``FlowObject``
    * ``Location``
    * ``Unit``

    **``data``**

    Provided ``data`` must include the ``label``. ``uri_suffix`` is optional, and can be inferred via ``label.lower().replace(" ", "_")``.

    Generates and writes the turtle file. Returns nothing.

    """
    BASE_TYPES = {
        "ActivityType": "brdfat",
        "FlowObject": "brdffo",
        "Location": "brdflo",
        "Unit": "brdfun",
    }

    if kind not in BASE_TYPES:
        raise ValueError("{} not in BONSAI base ontology types".format(kind))

    output_base_dir = Path(output_base_dir)

    base_uri = "http://rdf.bonsai.uno/" + kind.lower() + "/" + "/".join(directory_structure) + "/"

    g = add_common_elements(
        graph=Graph(),
        base_uri=base_uri,
        title=title,
        description=description,
        author=author,
        version=version,
    )

    lns = Namespace(base_uri)
    g.bind(BASE_TYPES[kind], base_uri)

    if custom_binds:
        for k, v in custom_binds.items():
            g.bind(k, v)

    for line in data:
        label = line[0]
        if len(line) == 1:
            uri = lns[line[0].lower().replace(" ", "_")]
        else:
            uri = lns[line[1]]
        if len(line) > 2:
            type_ = line[2]
        else:
            type_ = NS.nb[kind]

        node = URIRef(uri)
        g.add((node, RDF.type, type_))
        g.add((node, RDFS.label, Literal(label)))

    output_dir = output_base_dir
    if isinstance(kind, str):
        output_dir = output_dir / kind.lower()
    for subdir in directory_structure:
        output_dir = output_dir / subdir

    write_graph(output_dir, g)
