import os
import tempfile
import unittest

import py_dep_analysis as pda


class TestPyDepAnalysis(unittest.TestCase):
    def create_tmp_file(self, path: str, content: str):
        with open(path, "w") as f:
            f.write(content)

    def test_full_module_path(self):
        self.assertEqual(pda._full_module_path("aa.bb.cc", "__init__.py"), "aa.bb.cc")
        self.assertEqual(pda._full_module_path("aa.bb.cc", "dd.py"), "aa.bb.cc.dd")
        self.assertEqual(pda._full_module_path("", "dd.py"), "dd")

    def test_bazel_path_to_module_path(self):
        self.assertEqual(
            pda._bazel_path_to_module_path("//python/ray/rllib:xxx/yyy/dd"),
            "ray.rllib.xxx.yyy.dd",
        )
        self.assertEqual(
            pda._bazel_path_to_module_path("python:ray/rllib/xxx/yyy/dd"),
            "ray.rllib.xxx.yyy.dd",
        )
        self.assertEqual(
            pda._bazel_path_to_module_path("python/ray/rllib:xxx/yyy/dd"),
            "ray.rllib.xxx.yyy.dd",
        )

    def test_file_path_to_module_path(self):
        self.assertEqual(
            pda._file_path_to_module_path("python/ray/rllib/env/env.py"),
            "ray.rllib.env.env",
        )
        self.assertEqual(
            pda._file_path_to_module_path("python/ray/rllib/env/__init__.py"),
            "ray.rllib.env",
        )

    def test_import_line_continuation(self):
        graph = pda.DepGraph()
        graph.ids["ray"] = 0

        with tempfile.TemporaryDirectory() as tmpdir:
            src_path = os.path.join(tmpdir, "continuation1.py")
            self.create_tmp_file(
                src_path,
                """
import ray.rllib.env.\\
    mock_env
b = 2
""",
            )
            pda._process_file(graph, src_path, "ray")

        self.assertEqual(len(graph.ids), 2)
        print(graph.ids)
        # Shoud pick up the full module name.
        self.assertEqual(graph.ids["ray.rllib.env.mock_env"], 1)
        self.assertEqual(graph.edges[0], {1: True})

    def test_import_line_continuation_parenthesis(self):
        graph = pda.DepGraph()
        graph.ids["ray"] = 0

        with tempfile.TemporaryDirectory() as tmpdir:
            src_path = os.path.join(tmpdir, "continuation1.py")
            self.create_tmp_file(
                src_path,
                """
from ray.rllib.env import (ClassName,
    module1, module2)
b = 2
""",
            )
            pda._process_file(graph, src_path, "ray")

        self.assertEqual(len(graph.ids), 2)
        print(graph.ids)
        # Shoud pick up the full module name without trailing (.
        self.assertEqual(graph.ids["ray.rllib.env"], 1)
        self.assertEqual(graph.edges[0], {1: True})

    def test_from_import_file_module(self):
        graph = pda.DepGraph()
        graph.ids["ray"] = 0

        with tempfile.TemporaryDirectory() as tmpdir:
            src_path = "multi_line_comment_3.py"
            self.create_tmp_file(
                os.path.join(tmpdir, src_path),
                """
from ray.rllib.env import mock_env
a = 1
b = 2
""",
            )
            # Touch ray/rllib/env/mock_env.py in tmpdir,
            # so that it looks like a module.
            module_dir = os.path.join(tmpdir, "python", "ray", "rllib", "env")
            os.makedirs(module_dir, exist_ok=True)
            f = open(os.path.join(module_dir, "mock_env.py"), "w")
            f.write("print('hello world!')")
            f.close

            pda._process_file(graph, src_path, "ray", _base_dir=tmpdir)

        self.assertEqual(len(graph.ids), 2)
        self.assertEqual(graph.ids["ray.rllib.env.mock_env"], 1)
        # Only 1 edge from ray to ray.rllib.env.mock_env
        # ray.tune.tune is ignored.
        self.assertEqual(graph.edges[0], {1: True})

    def test_from_import_class_object(self):
        graph = pda.DepGraph()
        graph.ids["ray"] = 0

        with tempfile.TemporaryDirectory() as tmpdir:
            src_path = "multi_line_comment_3.py"
            self.create_tmp_file(
                os.path.join(tmpdir, src_path),
                """
from ray.rllib.env import MockEnv
a = 1
b = 2
""",
            )
            # Touch ray/rllib/env.py in tmpdir,
            # MockEnv is a class on env module.
            module_dir = os.path.join(tmpdir, "python", "ray", "rllib")
            os.makedirs(module_dir, exist_ok=True)
            f = open(os.path.join(module_dir, "env.py"), "w")
            f.write("print('hello world!')")
            f.close

            pda._process_file(graph, src_path, "ray", _base_dir=tmpdir)

        self.assertEqual(len(graph.ids), 2)
        # Should depend on env.py instead.
        self.assertEqual(graph.ids["ray.rllib.env"], 1)
        # Only 1 edge from ray to ray.rllib.env.mock_env
        # ray.tune.tune is ignored.
        self.assertEqual(graph.edges[0], {1: True})


if __name__ == "__main__":
    import pytest
    import sys

    sys.exit(pytest.main(["-v", __file__]))
