"""
Easy datalog.

Takes the core datalog engine and wraps it up so it's a little nicer to work with.

Easy because it's closer to hand, but no simpler.
"""

from typing import Sequence, Tuple

from datalog.evaluator import (
    join as __join,
    select as __select,
)
from datalog.reader import read as __read
from datalog.types import (
    Constant,
    Dataset,
    LTuple,
    LVar,
    PartlyIndexedDataset,
)


def read(text: str, db_cls=PartlyIndexedDataset):
    """A helper for reading Datalog text into a well-supported dataset."""

    return __read(text, db_cls=db_cls)


def q(t: Tuple[str]) -> LTuple:
    """Helper for writing terse queries.

    Takes a tuple of strings, and interprets them as a logic tuple.
    So you don't have to write the logic tuple out by hand.
    """

    def _x(s: str):
        if s[0].isupper():
            return LVar(s)
        else:
            return Constant(s)

    return tuple(_x(e) for e in t)


def __mapv(f, coll):
    return [f(e) for e in coll]


def __result(results_bindings):
    results, bindings = results_bindings
    return (
        tuple(tuple(c.value for c in e) for e in results),
        {var.name: c.value for var, c in bindings.items()},
    )


def select(db: Dataset, query: Tuple[str], bindings=None) -> Sequence[Tuple]:
    """Helper for interpreting tuples of strings as a query, and returning simplified results.

    Executes your query, returning matching full tuples.
    """

    return __mapv(__result, __select(db, q(query), bindings=bindings))


def join(db: Dataset, query: Sequence[Tuple[str]], bindings=None) -> Sequence[dict]:
    """Helper for interpreting a bunch of tuples of strings as a join query, and returning simplified
    results.

      Executes the query clauses as a join, returning a sequence of tuples and binding mappings such
      that the join constraints are simultaneously satisfied.


      >>> db = read('''
      ... edge(a, b).
      ... edge(b, c).
      ... edge(c, d).
      ... ''')
      >>> join(db, [
      ...   ('edge', 'A', 'B'),
      ...   ('edge', 'B', 'C')
      ... ])
      [((('edge', 'a', 'b'),
         ('edge', 'b', 'c')),
        {'A': 'a', 'B': 'b', 'C': 'c'}),
       ((('edge', 'b', 'c'),
         ('edge', 'c', 'd')),
        {'A': 'b', 'B': 'c', 'C': 'd'}),
       ((('edge', 'c', 'd'),
         ('edge', 'd', 'f')),
        {'A': 'c', 'B': 'd', 'C': 'f'})]
    """

    return __mapv(__result, __join(db, [q(c) for c in query], bindings=bindings))
