from pathlib import Path
from re import findall
from typing import Literal, Optional


class Reader:
    """EazyDocs Markdown Reader class to read markdown files and parse their
    contents.

    Notes:
        - The markdown file is expected to have been generated by EazyDocs.
    """

    def __init__(self, path: Path):
        """EazyDocs Markdown Reader class to read markdown files and parse their contents.

        Args:
            path (Path): Path object pointing to the markdown file to be read.
        """
        self.path = path

        self.methods = dict()

    def __enter__(self, path: Optional[Path] = None):
        """Context manager interface for the Reader class. Reads the markdown
        file and prepares it for parsing.

        1. Reads the markdown file from the provided path.
        2. Extracts methods from the markdown file contents.



        Args:
            path (Optional[Path], optional): Path to the markdown file to be
                read. If not provided, uses the path set during initialization.
                Defaults to None.
        """
        if path is None:
            path = self.path

        with open(path, "r", encoding="utf-8") as file:
            self.contents = file.read()

        self._get_methods()

        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> Literal[False]:
        """Context manager interface for the Reader class."""
        return False

    def _get_methods(self) -> list[str] | None:
        pattern = r"\<strong .+\>(.+)</strong>"
        methods = findall(pattern, self.contents)
        if methods == []:
            return None

        for method in methods:
            params = self._parse_params(method)
            if params is not None:
                self.methods.update({method: params})

    def _parse_params(self, method: str) -> dict | None:
        """Parses the parameters of a method from the markdown contents.

        Notes:
            - This method extracts parameter names and descriptions for a given
              method from the markdown contents.
            - It uses regular expressions to find parameter names and their
              corresponding descriptions.
                - Markdown files generated by EazyDocs follow a specific format
                  where parameters are defined as `method-paramName'` and their
                  descriptions as `method-paramName-description'.`

        Args:
            method (str): The method name to parse parameters for.

        Returns:
            dict | None: A dictionary containing parameter names as keys and
                their descriptions as values. Returns None if no parameters are
                found for the method.
        """
        # Get parameter names
        pattern = rf"{method}\-(.*)\'"
        param_names = findall(pattern, self.contents)
        param_names = [p for p in param_names if "-description" not in p]
        # Return None if no parameters found
        if param_names == []:
            return None
        # Get parameter descriptions and build dictionary
        params = dict()

        for param in param_names:
            pattern = rf"{param}-description'>(.+)\."
            description = findall(pattern, self.contents)

            if description != []:
                params.update({param: description[0]})
            else:
                params.update({param: None})
        return params

    @property
    def contents(self) -> str:
        """Contents of the markdown file."""
        return self._contents

    @contents.setter
    def contents(self, val: str) -> None:
        self._contents = val

    @property
    def methods(self) -> dict[str, dict[str, str]]:
        """Methods parsed from the markdown file.

        Returns:
            dict: A dictionary where keys are method names
                and values are dictionaries of parameter names and their
                descriptions.
        """
        return self._methods

    @methods.setter
    def methods(self, val: dict[str, dict[str, str]]) -> None:
        self._methods = val
