import os
import subprocess
import tempfile
import shutil

from sentry_sdk.consts import VERSION as SDK_VERSION
from sentry_sdk._types import MYPY

if MYPY:
    from typing import Union


class PackageBuilder:
    def __init__(
        self,
        base_dir,  # type: str
        pkg_parent_dir,  # type: str
        dist_rel_path,  # type: str
    ):
        # type: (...) -> None
        self.base_dir = base_dir
        self.pkg_parent_dir = pkg_parent_dir
        self.dist_rel_path = dist_rel_path
        self.packages_dir = self.get_relative_path_of(pkg_parent_dir)

    def make_directories(self):
        # type: (...) -> None
        os.makedirs(self.packages_dir)

    def install_python_binaries(self):
        # type: (...) -> None
        wheels_filepath = os.path.join(
            self.dist_rel_path, f"sentry_sdk-{SDK_VERSION}-py2.py3-none-any.whl"
        )
        subprocess.run(
            [
                "pip",
                "install",
                "--no-cache-dir",  # Disables the cache -> always accesses PyPI
                "-q",  # Quiet
                wheels_filepath,  # Copied to the target directory before installation
                "-t",  # Target directory flag
                self.packages_dir,
            ],
            check=True,
        )

    def create_init_serverless_sdk_package(self):
        # type: (...) -> None
        """
        Method that creates the init_serverless_sdk pkg in the
        sentry-python-serverless zip
        """
        serverless_sdk_path = (
            f"{self.packages_dir}/sentry_sdk/" f"integrations/init_serverless_sdk"
        )
        if not os.path.exists(serverless_sdk_path):
            os.makedirs(serverless_sdk_path)
        shutil.copy(
            "scripts/init_serverless_sdk.py", f"{serverless_sdk_path}/__init__.py"
        )

    def zip(
        self, filename  # type: str
    ):
        # type: (...) -> None
        subprocess.run(
            [
                "zip",
                "-q",  # Quiet
                "-x",  # Exclude files
                "**/__pycache__/*",  # Files to be excluded
                "-r",  # Recurse paths
                filename,  # Output filename
                self.pkg_parent_dir,  # Files to be zipped
            ],
            cwd=self.base_dir,
            check=True,  # Raises CalledProcessError if exit status is non-zero
        )

    def get_relative_path_of(
        self, subfile  # type: str
    ):
        # type: (...) -> str
        return os.path.join(self.base_dir, subfile)


# Ref to `pkg_parent_dir` Top directory in the ZIP file.
# Placing the Sentry package in `/python` avoids
# creating a directory for a specific version. For more information, see
#  https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-path
def build_packaged_zip(
    dist_rel_path="dist",  # type: str
    dest_zip_filename=f"sentry-python-serverless-{SDK_VERSION}.zip",  # type: str
    pkg_parent_dir="python",  # type: str
    dest_abs_path=None,  # type: Union[str, None]
):
    # type: (...) -> None
    if dest_abs_path is None:
        dest_abs_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), "..", dist_rel_path)
        )
    with tempfile.TemporaryDirectory() as tmp_dir:
        package_builder = PackageBuilder(tmp_dir, pkg_parent_dir, dist_rel_path)
        package_builder.make_directories()
        package_builder.install_python_binaries()
        package_builder.create_init_serverless_sdk_package()
        package_builder.zip(dest_zip_filename)
        if not os.path.exists(dist_rel_path):
            os.makedirs(dist_rel_path)
        shutil.copy(
            package_builder.get_relative_path_of(dest_zip_filename), dest_abs_path
        )


if __name__ == "__main__":
    build_packaged_zip()
