import os
import shutil
import subprocess
import sys
import tempfile
from typing import TYPE_CHECKING

from sentry_sdk.consts import VERSION as SDK_VERSION

if TYPE_CHECKING:
    from typing import Optional

DIST_PATH = "dist"  # created by "make dist" that is called by "make aws-lambda-layer"
PYTHON_SITE_PACKAGES = "python"  # see https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-path


class LayerBuilder:
    def __init__(
        self,
        base_dir,  # type: str
        out_zip_filename=None,  # type: Optional[str]
    ):
        # type: (...) -> None
        self.base_dir = base_dir
        self.python_site_packages = os.path.join(self.base_dir, PYTHON_SITE_PACKAGES)
        self.out_zip_filename = (
            f"sentry-python-serverless-{SDK_VERSION}.zip"
            if out_zip_filename is None
            else out_zip_filename
        )

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

    def install_python_packages(self):
        # type: (...) -> None
        # Install requirements for Lambda Layer (these are more limited than the SDK requirements,
        # because Lambda does not support the newest versions of some packages)
        subprocess.check_call(
            [
                sys.executable,
                "-m",
                "pip",
                "install",
                "-r",
                "requirements-aws-lambda-layer.txt",
                "--target",
                self.python_site_packages,
            ],
        )

        sentry_python_sdk = os.path.join(
            DIST_PATH,
            f"sentry_sdk-{SDK_VERSION}-py2.py3-none-any.whl",  # this is generated by "make dist" that is called by "make aws-lambda-layer"
        )
        subprocess.run(
            [
                "pip",
                "install",
                "--no-cache-dir",  # always access PyPI
                "--no-deps",  # the right depencencies have been installed in the call above
                "--quiet",
                sentry_python_sdk,
                "--target",
                self.python_site_packages,
            ],
            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.python_site_packages}/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):
        # type: (...) -> None
        subprocess.run(
            [
                "zip",
                "-q",  # Quiet
                "-x",  # Exclude files
                "**/__pycache__/*",  # Files to be excluded
                "-r",  # Recurse paths
                self.out_zip_filename,  # Output filename
                PYTHON_SITE_PACKAGES,  # Files to be zipped
            ],
            cwd=self.base_dir,
            check=True,  # Raises CalledProcessError if exit status is non-zero
        )

        shutil.copy(
            os.path.join(self.base_dir, self.out_zip_filename),
            os.path.abspath(DIST_PATH),
        )


def build_packaged_zip(base_dir=None, make_dist=False, out_zip_filename=None):
    if base_dir is None:
        base_dir = tempfile.mkdtemp()

    if make_dist:
        # Same thing that is done by "make dist"
        # (which is a dependency of "make aws-lambda-layer")
        subprocess.check_call(
            [sys.executable, "setup.py", "sdist", "bdist_wheel", "-d", DIST_PATH],
        )

    layer_builder = LayerBuilder(base_dir, out_zip_filename=out_zip_filename)
    layer_builder.make_directories()
    layer_builder.install_python_packages()
    layer_builder.create_init_serverless_sdk_package()
    layer_builder.zip()

    # Just for debugging
    dist_path = os.path.abspath(DIST_PATH)
    print("Created Lambda Layer package with this information:")
    print(" - Base directory for generating package: {}".format(layer_builder.base_dir))
    print(
        " - Created Python SDK distribution (in `{}`): {}".format(dist_path, make_dist)
    )
    if not make_dist:
        print("    If 'False' we assume it was already created (by 'make dist')")
    print(" - Package zip filename: {}".format(layer_builder.out_zip_filename))
    print(" - Copied package zip to: {}".format(dist_path))


if __name__ == "__main__":
    build_packaged_zip()
