"""Utility Class for Getting Function or Layer Manifest Dependency Hashes"""
import pathlib

from typing import Any, Optional

from samcli.lib.build.workflow_config import get_workflow_config
from samcli.lib.utils.hash import file_checksum

# TODO Expand this class to hash specific sections of the manifest
class DependencyHashGenerator:
    _code_uri: str
    _base_dir: str
    _code_dir: str
    _runtime: str
    _manifest_path_override: Optional[str]
    _hash_generator: Any
    _calculated: bool
    _hash: Optional[str]

    def __init__(
        self,
        code_uri: str,
        base_dir: str,
        runtime: str,
        manifest_path_override: Optional[str] = None,
        hash_generator: Any = None,
    ):
        """
        Parameters
        ----------
        code_uri : str
            Relative path specified in the function/layer resource
        base_dir : str
            Absolute path which the function/layer dir is located
        runtime : str
            Runtime of the function/layer
        manifest_path_override : Optional[str], optional
            Override default manifest path for each runtime, by default None
        hash_generator : Any, optional
            Hash generation function. Can be hashlib.md5(), hashlib.sha256(), etc, by default None
        """
        self._code_uri = code_uri
        self._base_dir = base_dir
        self._code_dir = str(pathlib.Path(self._base_dir, self._code_uri).resolve())
        self._runtime = runtime
        self._manifest_path_override = manifest_path_override
        self._hash_generator = hash_generator
        self._calculated = False
        self._hash = None

    def _calculate_dependency_hash(self) -> Optional[str]:
        """Calculate the manifest file hash

        Returns
        -------
        Optional[str]
            Returns manifest hash. If manifest does not exist or not supported, None will be returned.
        """
        if self._manifest_path_override:
            manifest_file = self._manifest_path_override
        else:
            config = get_workflow_config(self._runtime, self._code_dir, self._base_dir)
            manifest_file = config.manifest_name

        if not manifest_file:
            return None

        manifest_path = pathlib.Path(self._code_dir, manifest_file).resolve()
        if not manifest_path.is_file():
            return None

        return file_checksum(str(manifest_path), hash_generator=self._hash_generator)

    @property
    def hash(self) -> Optional[str]:
        """
        Returns
        -------
        Optional[str]
            Hash for dependencies in the manifest.
            If the manifest does not exist or not supported, this value will be None.
        """
        if not self._calculated:
            self._hash = self._calculate_dependency_hash()
            self._calculated = True
        return self._hash
