#!/usr/bin/env python3
"""
Welcome to Cloud Sync!
This script downloads a wheel file from a URL, installs it
and runs the cloud_sync module.

The goal of this script is to be kept at a MINIMUM in size, and
bundle only the necessary logic to run the cloud_sync module.

Please do not change this script,
as it sets up important security and functionalities.
"""

# ONLY IMPORT STDLIB STUFF HERE OR STUFF WILL BREAK!
import logging
import os
import site
import ssl
import subprocess
import sys
import urllib.request
from importlib import reload

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    handlers=[logging.StreamHandler(sys.stdout)],
)
logger = logging.getLogger("loader")

# https://downloads.zivver.com/cloudsync/cloud_sync-1-py3-none-any.whl
# https://downloads.zivver.com/cloudsync/loader.py
# https://downloads.zivver.com/cloudsync/setup.ps1

WHEEL_URL = "https://downloads.zivver.com/cloudsync/cloud_sync-1-py3-none-any.whl"


def main():
    wheel_file = "dist_script/cloud_sync-1-py3-none-any.whl"
    logger.info(f"Downloading {WHEEL_URL} to {wheel_file}")
    mkdir_if_not_exists(wheel_file)
    download_wheel(wheel_file)
    logger.info(f"Installing {wheel_file}")
    pip_install(wheel_file)
    logger.info("Starting Cloud Sync. It may take up to a minute to start.")
    run_cloud_sync()
    logger.info("Cloud Sync finished running!")


def mkdir_if_not_exists(path: str):
    dirs = os.path.dirname(path)
    if not os.path.exists(dirs):
        os.makedirs(dirs)


def download_wheel(out_file: str):
    assert (
        WHEEL_URL.startswith("https://")
        or WHEEL_URL.startswith("http://host.docker.internal")
        or WHEEL_URL.startswith("http://localhost")
    ), "Only HTTPS or localhost allowed"

    with urllib.request.urlopen(WHEEL_URL, timeout=20, context=get_ssl_context()) as f:
        logger.info(f"Dynamically loaded script hash: {f.info()['ETag']}")
        with open(out_file, "wb") as out:
            out.write(f.read())


def pip_install(package):
    subprocess.check_call(
        [
            sys.executable,
            "-m",
            "pip",
            "install",
            "-q",
            "--no-cache-dir",
            "--upgrade",
            "pip",
        ]
    )
    subprocess.check_call(
        [sys.executable, "-m", "pip", "install", "-q", "--no-cache-dir", package]
    )

    # This reloads the sys.path, which is how `import` works. It allows us to dynamically
    # import packages after pip installing them programmatically.
    reload(site)


def run_cloud_sync():
    subprocess.check_call(
        [sys.executable, "-m", "cloud_sync.main"] + sys.argv,
        stderr=subprocess.STDOUT,
        env=os.environ,
        universal_newlines=True,
    )


def get_ssl_context() -> ssl.SSLContext:
    # Do not change this function!
    # Some Azure Automate sandboxes don't embed a CA bundle, which we need for security reasons.
    # https://docs.python.org/3.10/library/ssl.html#ssl-security
    #
    # We deliberately do NOT key this on the operating system. Azure runs Python 3.8 runbooks on
    # Windows sandboxes and Python 3.10 runbooks on Linux ones, so os.name tells us nothing about
    # whether a usable trust store exists. Instead we build the default context and ask it how many
    # CA certificates it actually loaded; only if it loaded none do we fall back to certifi.
    ssl_context = ssl.create_default_context()

    if ssl_context.cert_store_stats()["x509_ca"] == 0:
        logger.info("No system CA certificates found, installing certifi")
        pip_install("certifi")
        import certifi

        ssl_context = ssl.create_default_context(cafile=certifi.where())
        assert (
            ssl_context.cert_store_stats()["x509_ca"] > 0
        ), "no CA certificates loaded from certifi"

    assert ssl_context.verify_mode == ssl.CERT_REQUIRED, "CERT_REQUIRED not set"
    assert ssl_context.check_hostname, "check_hostname not set"
    return ssl_context


if __name__ == "__main__":
    main()
