# /********************************************************************************
# * Copyright (c) 2023 Contributors to the Eclipse Foundation
# *
# * See the NOTICE file(s) distributed with this work for additional
# * information regarding copyright ownership.
# *
# * This program and the accompanying materials are made available under the
# * terms of the Apache License 2.0 which is available at
# * http://www.apache.org/licenses/LICENSE-2.0
# *
# * SPDX-License-Identifier: Apache-2.0
# ********************************************************************************/
import setuptools

try:
    from setuptools.command import build
except ImportError:
    from distutils.command import build  # pylint: disable=deprecated-module
from setuptools.command import build_py
from setuptools.command import sdist
from setuptools.command.develop import develop as _develop


class BuildGenerateProtos(setuptools.Command):
    def run(self):
        self.run_command('generate_proto')
        return super().run()


class GenerateProtosCommand(setuptools.Command):
    """Command to run prototagandcopy.py script."""
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        import subprocess  # pylint: disable=import-outside-toplevel
        import os
        from pathlib import Path

        # Check if we're in an sdist build (proto files should already exist)
        proto_files_exist = any(Path('.').glob('**/*_pb2.py'))
        proto_dir_exists = Path("../submodules/kuksa-proto/proto/").exists()

        if proto_files_exist:
            print("Proto files already exist, skipping package tagging")
            # List files in the current directory
            print("Existing proto files:")
            for file in Path('.').glob('**/*_pb2.py'):
                print(f" - {file}")
            return

        if not proto_dir_exists:
            print("Warning: Proto directory not found, skipping package tagging")
            return

        print(f"Generating package from proto: {os.getcwd()}")
        result = subprocess.call(['python', 'prototagandcopy.py'])
        if result != 0:
            print(f"Warning: prototagandcopy.py failed with exit code {result}")
        # print("This is how it looks like:")
        # for file in Path('./kuksa').glob('**/*'):
        #    print(f" - {file}")


class BuildPackageProtos(setuptools.Command):
    def run(self):
        self.run_command('build_pb2')
        return super().run()


class BuildPackageProtosCommand(setuptools.Command):
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        import subprocess
        import sys
        from pathlib import Path

        # Check if proto files already exist (generated by previous step)
        proto_files_exist = any(Path('.').glob('**/*_pb2.py'))
        if proto_files_exist:
            print("Proto files already exist, skipping protobuf compilation")
            # Recursively list every file in folder "kuksa"
            for file in Path('./kuksa').glob('**/*'):
                print(f" - {file}")
            return

        # Check if we have .proto files to compile
        proto_source_files = list(Path('.').glob('**/*.proto'))
        if not proto_source_files:
            print("No .proto files found, skipping protobuf compilation")
            return

        print(f"Found {len(proto_source_files)} .proto files to compile")

        try:
            from grpc_tools import command
            print("Compiling protobuf files...")
            command.build_package_protos(".", strict_mode=True)
        except ImportError:
            # Fallback to direct protoc call
            print("grpc_tools not found, using protoc directly.")
            if proto_source_files:
                subprocess.check_call([
                    sys.executable, "-m", "grpc_tools.protoc",
                    "--proto_path=.",
                    "--python_out=.",
                    "--grpc_python_out=.",
                    *[str(f) for f in proto_source_files]
                ])


class BuildCommand(BuildGenerateProtos, BuildPackageProtos, build.build):
    ...


class BuildPyCommand(BuildGenerateProtos, BuildPackageProtos, build_py.build_py):  # pylint: disable=too-many-ancestors
    def finalize_options(self):
        # First run the parent finalize_options which includes proto generation
        super().finalize_options()

        # After proto generation, ensure packages are properly set BEFORE build_py runs
        self.ensure_proto_packages()

    def run(self):
        # Just run the normal build_py
        build_py.build_py.run(self)

    def ensure_proto_packages(self):
        """Ensure generated proto packages are included in the distribution."""
        import os

        # Find all proto packages
        print("Searching and adding dynamically built proto packages...")
        proto_packages = []

        for root, dirs, files in os.walk('.'):
            if '__init__.py' in files:
                package = root.replace('./', '').replace('/', '.').lstrip('.')
                print(f"Found package: {package}")
                if package and any(proto_name in package for proto_name in ['kuksa', 'sdv']):
                    # Skip build directories
                    if not package.startswith('build.'):
                        proto_packages.append(package)
                        print(f"Adding proto package: {package}")

        # Get current packages from distribution or setup.cfg
        current_packages = []
        if hasattr(self.distribution, 'packages') and self.distribution.packages:
            current_packages = list(self.distribution.packages)

        # Add proto packages
        all_packages = current_packages + [pkg for pkg in proto_packages if pkg not in current_packages]

        # Update the distribution packages
        self.distribution.packages = all_packages
        print(f"Final package list: {all_packages}")


class SDistCommand(BuildGenerateProtos, BuildPackageProtos, sdist.sdist):
    ...


class DevelopCommand(BuildGenerateProtos, BuildPackageProtos, _develop):

    def run(self):
        try:
            self.run_command("generate_proto")
            self.run_command("build_pb2")
        except Exception as e:
            print(f"Warning: Proto generation failed: {e}")
            print("Continuing with development install...")
        super().run()


setuptools.setup(
    cmdclass={
        "generate_proto": GenerateProtosCommand,
        "build": BuildCommand,
        "build_pb2": BuildPackageProtosCommand,
        "build_py": BuildPyCommand,  # Used for editable installs but also for building wheels
        "sdist": SDistCommand,
        "develop": DevelopCommand,  # Also handle editable installs
    }
)
