"""
Action to build a specific Makefile target
"""

import logging
from pathlib import Path

from aws_lambda_builders.actions import ActionFailedError, BaseAction, Purpose

from .exceptions import MakeFileNotFoundError
from .make import MakeExecutionError

LOG = logging.getLogger(__name__)


class CustomMakeAction(BaseAction):
    """
    A Lambda Builder Action that builds and packages a provided runtime project using Make.
    """

    NAME = "MakeBuild"
    DESCRIPTION = "Running build target on Makefile"
    PURPOSE = Purpose.COMPILE_SOURCE

    def __init__(
        self,
        artifacts_dir,
        manifest_path,
        osutils,
        subprocess_make,
        build_logical_id,
        working_directory,
    ):
        """
        :type artifacts_dir: str
        :param artifacts_dir: directory where artifacts needs to be stored.

        :type manifest_path: str
        :param manifest_path: path to Makefile of an Make project with the source in same folder.

        :type osutils: aws_lambda_builders.workflows.custom_make.utils.OSUtils
        :param osutils: An instance of OS Utilities for file manipulation

        :type subprocess_make aws_lambda_builders.workflows.custom_make.make.SubprocessMake
        :param subprocess_make: An instance of the Make process wrapper

        :type build_logical_id: str
        :param build_logical_id: the lambda resource logical id that will be built by the custom action.

        :type working_directory: str
        :param working_directory: path to the working directory where the Makefile will be executed.
        """
        super(CustomMakeAction, self).__init__()
        self.artifacts_dir = artifacts_dir
        self.manifest_path = manifest_path
        self.osutils = osutils
        self.subprocess_make = subprocess_make
        self.build_logical_id = build_logical_id
        self.working_directory = working_directory

    @property
    def artifact_dir_path(self):
        # This is required when running on windows to determine if we are running in linux
        # subsystem or on native cmd or powershell.
        if self.osutils.is_windows():
            return Path(self.artifacts_dir).as_posix() if self.osutils.which("sh") else self.artifacts_dir
        else:
            return self.artifacts_dir

    def manifest_check(self):
        # Check for manifest file presence and if not present raise MakefileNotFoundError
        if not self.osutils.exists(self.manifest_path):
            raise MakeFileNotFoundError("Makefile not found at {}".format(self.manifest_path))

    def execute(self):
        """
        Runs the action.

        :raises lambda_builders.actions.ActionFailedError: when Make Build fails.
        """

        # Check for manifest file
        try:
            self.manifest_check()
        except MakeFileNotFoundError as ex:
            raise ActionFailedError(str(ex))

        # Create the Artifacts Directory if it doesnt exist.
        if not self.osutils.exists(self.artifacts_dir):
            self.osutils.makedirs(self.artifacts_dir)

        try:
            current_env = self.osutils.environ()
            LOG.info("%s: Current Artifacts Directory : %s", self.build_logical_id, self.artifact_dir_path)
            current_env.update({"ARTIFACTS_DIR": self.artifact_dir_path})
            # Export environmental variables that might be needed by other binaries used
            # within the Makefile and also specify the makefile to be used as well.
            self.subprocess_make.run(
                [
                    "--makefile",
                    "{}".format(self.manifest_path),
                    "build-{logical_id}".format(logical_id=self.build_logical_id),
                ],
                env=current_env,
                cwd=self.working_directory,
            )
        except MakeExecutionError as ex:
            raise ActionFailedError(str(ex))
