from typing import Iterable

from clarabot_nano.utility.path.tracker import PathDependencyTracker


class _TestingPathDependencyTracker(PathDependencyTracker):
    def assert_store(self, upstream: Iterable[str], downstream: Iterable[str]):
        upstream = set(upstream)
        downstream = set(downstream)
        self_upstream = set(self._affects_upstream)
        self_downstream = set(self._affects_downstream)
        assert self_upstream == upstream and self_downstream == downstream, (
            f"Mismatch:"
            f"\nUpstream  :\n  {self_upstream.difference(upstream)}\n  {upstream.difference(self_upstream)}"
            f"\nDownstream:\n  {self_downstream.difference(downstream)}\n  {downstream.difference(self_downstream)}"
        )


def test_utility_path_dependency_tracker():
    pdt = _TestingPathDependencyTracker()

    pdt.assert_store([], [])
    for p, a in {
        "/": [],
        "/aa": [],
        "/cc/c": [],
        "/cc/cc/cc": [],
        "/x": [],
    }.items():
        r = sorted(pdt.affects(p))
        assert r == a, f"Affects mismatch: {p}\n{r}\n{a}"

    # upstream
    pdt.track_upstream("/ab")
    pdt.track_upstream("/cc/ca")
    pdt.track_upstream("/cc/cc/ca")
    pdt.track_upstream("/cc/cc/ce")
    pdt.track_upstream("/dd")
    pdt.assert_store(["/ab", "/cc/ca", "/cc/cc/ca", "/cc/cc/ce", "/dd"], [])
    for p, a in {
        "/": [],
        "/aa": [],
        "/cc": ["/cc/ca", "/cc/cc/ca", "/cc/cc/ce"],
        "/cc/c": [],
        "/cc/ca": ["/cc/ca"],
        "/cc/ca/cc": [],
        "/cc/cc/cc": [],
        "/x": [],
    }.items():
        r = sorted(pdt.affects(p))
        assert r == a, f"Affects mismatch: {p}\n{r}\n{a}"

    # downstream
    pdt.clear()
    pdt.assert_store([], [])
    pdt.track_downstream("/ab")
    pdt.track_downstream("/cc/ca")
    pdt.track_downstream("/cc/cc/ca")
    pdt.track_downstream("/cc/cc/ce")
    pdt.track_downstream("/dd")
    pdt.assert_store([], ["/ab", "/cc/ca", "/cc/cc/ca", "/cc/cc/ce", "/dd"])
    for p, a in {
        "/": [],
        "/aa": [],
        "/cc": [],
        "/cc/c": [],
        "/cc/ca": ["/cc/ca"],
        "/cc/ca/cc": ["/cc/ca"],
        "/cc/cc/cc": [],
        "/x": [],
    }.items():
        r = sorted(pdt.affects(p))
        assert r == a, f"Affects mismatch: {p}\n{r}\n{a}"

    # both
    pdt.clear()
    pdt.assert_store([], [])
    pdt.track("/ab")
    pdt.track("/cc/ca")
    pdt.track("/cc/cc/ca")
    pdt.track("/cc/cc/ce")
    pdt.track("/dd")
    pdt.assert_store(
        ["/ab", "/cc/ca", "/cc/cc/ca", "/cc/cc/ce", "/dd"], ["/ab", "/cc/ca", "/cc/cc/ca", "/cc/cc/ce", "/dd"]
    )
    for p, a in {
        "/": [],
        "/aa": [],
        "/cc": ["/cc/ca", "/cc/cc/ca", "/cc/cc/ce"],
        "/cc/c": [],
        "/cc/ca": ["/cc/ca", "/cc/ca"],
        "/cc/ca/cc": ["/cc/ca"],
        "/cc/cc/cc": [],
        "/x": [],
    }.items():
        r = sorted(pdt.affects(p))
        assert r == a, f"Affects mismatch: {p}\n{r}\n{a}"

    pdt.release("/cc/ca")
    pdt.assert_store(["/ab", "/cc/cc/ca", "/cc/cc/ce", "/dd"], ["/ab", "/cc/cc/ca", "/cc/cc/ce", "/dd"])
    for p, a in {
        "/": [],
        "/aa": [],
        "/cc": ["/cc/cc/ca", "/cc/cc/ce"],
        "/cc/c": [],
        "/cc/ca": [],
        "/cc/ca/cc": [],
        "/cc/cc/cc": [],
        "/x": [],
    }.items():
        r = sorted(pdt.affects(p))
        assert r == a, f"Affects mismatch: {p}\n{r}\n{a}"
