"""Setuptools script for Grapl graph-descriptions"""

import os
import shutil
import subprocess
import sys

from distutils.spawn import find_executable

from distutils.command.build import build as _build
from distutils.command.clean import clean as _clean

from setuptools.command.build_py import build_py as _build_py
from setuptools.command.develop import develop as _develop
from setuptools.command.egg_info import egg_info as _egg_info
from setuptools.command.install import install as _install
from setuptools.command.sdist import sdist as _sdist
from setuptools.command.test import test as _test
from setuptools import setup

from wheel.bdist_wheel import bdist_wheel as _bdist_wheel

HERE = os.path.abspath(os.path.dirname(__file__))
PROTO_DIR = os.path.join(HERE, "proto")
OUT_DIR_NAME = "grapl_graph_descriptions"
OUT_DIR = os.path.join(HERE, OUT_DIR_NAME)


def is_comment(line):
    """check whether a line is a comment"""
    return line.strip().startswith("#")


def find_requirements():
    with open(os.path.join(HERE, "requirements.txt")) as requirements:
        return tuple(line.strip() for line in requirements if not is_comment(line))


def find_version():
    with open(os.path.join(HERE, "VERSION")) as version:
        return version.read().strip()


def find_protoc():
    """find the protobuf compiler

    returns absolute path to protoc, raises an exception if absent
    """
    if "PROTOC" in os.environ:
        return os.environ["PROTOC"]
    protoc = find_executable("protoc")
    if protoc is None:
        raise Exception("Could not locate protoc")
    return protoc


def compile_protobuf(proto_file, proto_dir, out_dir):
    """compile the .proto file to a *_pb2.py file in out_dir"""
    protoc = find_protoc()
    sys.stdout.write(f"compiling protobuf {proto_file}\n")
    if (
        subprocess.call(
            [protoc, f"--proto_path={proto_dir}", f"--python_out={out_dir}", proto_file]
        )
        != 0
    ):
        raise Exception(f"Failed to compile protobuf {proto_file}")


def find_proto_files():
    """find all the .proto files"""
    for (base_path, _, files) in os.walk(PROTO_DIR):
        for file_name in files:
            if file_name.endswith(".proto"):
                yield os.path.join(base_path, file_name)


def compile_all_protobufs():
    """walk the proto directory tree and compile every .proto file"""
    if not os.path.exists(OUT_DIR):
        os.mkdir(OUT_DIR)
    for proto_file in find_proto_files():
        compile_protobuf(proto_file, PROTO_DIR, OUT_DIR)
    # build a package structure in the OUT_DIR
    for (base_path, _, files) in os.walk(OUT_DIR):
        if "__init__.py" not in files:
            with open(os.path.join(base_path, "__init__.py"), "w") as init:
                init.write("# generated by grapl-graph-descriptions/setup.py\n")
                init.write("import sys\n")
                init.write("from pathlib import Path\n\n")
                init.write("HERE = str(Path(__file__).parent)\n")
                init.write("if HERE not in sys.path:\n")
                init.write("    sys.path.append(HERE)\n")


class bdist_wheel(_bdist_wheel):
    def run(self):
        compile_all_protobufs()
        _bdist_wheel.run(self)


class build(_build):
    def run(self):
        compile_all_protobufs()
        _build.run(self)


class build_py(_build_py):
    def run(self):
        compile_all_protobufs()
        _build_py.run(self)


class clean(_clean):
    def run(self):
        if os.path.exists(OUT_DIR):
            shutil.rmtree(OUT_DIR)
        build_dir = os.path.join(HERE, "build")
        if os.path.exists(build_dir):
            shutil.rmtree(build_dir)
        dist_dir = os.path.join(HERE, "dist")
        if os.path.exists(dist_dir):
            shutil.rmtree(dist_dir)
        egg_info_dir = os.path.join(HERE, "grapl_graph_descriptions.egg-info")
        if os.path.exists(egg_info_dir):
            shutil.rmtree(egg_info_dir)
        _clean.run(self)


class develop(_develop):
    def run(self):
        compile_all_protobufs()
        _develop.run(self)


class egg_info(_egg_info):
    def run(self):
        compile_all_protobufs()
        _egg_info.run(self)


class install(_install):
    def run(self):
        compile_all_protobufs()
        _install.run(self)


class sdist(_sdist):
    def run(self):
        compile_all_protobufs()
        _sdist.run(self)


class test(_test):
    def run(self):
        compile_all_protobufs()
        _test.run(self)


__version__ = find_version()

setup(
    name="grapl_graph_descriptions",
    version=__version__,
    author="Grapl, Inc.",
    author_email="grapl.code@graplsecurity.com",
    url="https://github.com/grapl-security/grapl",
    description="Grapl protobuf definitions",
    packages=(OUT_DIR_NAME,),
    zip_safe=False,
    cmdclass={
        "bdist_wheel": bdist_wheel,
        "build": build,
        "build_py": build_py,
        "clean": clean,
        "develop": develop,
        "egg_info": egg_info,
        "install": install,
        "sdist": sdist,
        "test": test,
    },
    install_requires=find_requirements(),
    setup_requires=("wheel",),
)
