import asyncio

import libcst as cst

from py_agent.models.testable import TestablesDTO
from py_agent.utils.file_io import read_multiple_files
from py_agent.visitors.testables_visitor import TestablesVisitor


class TestablesExtractor:
    @staticmethod
    def get_testables_from_contents(
        contents: str,
        absolute_file_path: str,
        relative_file_path: str,
        project_path: str,
    ) -> TestablesDTO:
        module = cst.parse_module(contents)
        metadata_wrapper = cst.MetadataWrapper(module)

        visitor = TestablesVisitor(
            module=module,
            metadata_wrapper=metadata_wrapper,
            absolute_file_path=absolute_file_path,
            relative_file_path=relative_file_path,
            project_path=project_path,
        )
        metadata_wrapper.visit(visitor)

        return TestablesDTO(
            file_path=absolute_file_path,
            testables=visitor.testables,
        )

    @staticmethod
    def get_testables(
        absolute_file_path: str, relative_file_path: str, project_path: str
    ) -> TestablesDTO:
        with open(absolute_file_path, encoding="utf-8") as f:
            file_code = f.read()
        return TestablesExtractor.get_testables_from_contents(
            file_code, absolute_file_path, relative_file_path, project_path
        )

    @staticmethod
    def get_testables_from_multiple_files(
        file_paths: list[dict[str, str]], project_path: str
    ) -> dict[str, TestablesDTO]:
        """file_paths: list of dicts with 'absolute_file_path' and 'relative_file_path' keys"""
        absolute_paths = [p["absolute_file_path"] for p in file_paths]
        relative_paths = [p["relative_file_path"] for p in file_paths]

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            contents = loop.run_until_complete(read_multiple_files(absolute_paths, relative_paths))
        finally:
            loop.close()

        result = {}
        for rel_path, entry in contents.items():
            testables_dto = TestablesExtractor.get_testables_from_contents(
                entry["content"], entry["absolute_path"], entry["relative_path"], project_path
            )
            result[rel_path] = testables_dto

        return result
