"""The logic for Flake8's integration with setuptools."""
import os
from typing import List

import setuptools

from flake8.main import application as app

UNSET = object()


class Flake8(setuptools.Command):
    """Run Flake8 via setuptools/distutils for registered modules."""

    description = "Run Flake8 on modules registered in setup.py"
    # NOTE(sigmavirus24): If we populated this with a list of tuples, users
    # could do something like ``python setup.py flake8 --ignore=E123,E234``
    # but we would have to redefine it and we can't define it dynamically.
    # Since I refuse to copy-and-paste the options here or maintain two lists
    # of options, and since this will break when users use plugins that
    # provide command-line options, we are leaving this empty. If users want
    # to configure this command, they can do so through config files.
    user_options = []  # type: List[str]

    def initialize_options(self):
        """Override this method to initialize our application."""
        self.flake8 = app.Application()
        self.flake8.initialize([])
        options = self.flake8.option_manager.options
        for option in options:
            if option.parse_from_config:
                setattr(self, option.config_name, UNSET)

    def finalize_options(self):
        """Override this to parse the parameters."""
        options = self.flake8.option_manager.options
        for option in options:
            if option.parse_from_config:
                name = option.config_name
                value = getattr(self, name, UNSET)
                if value is UNSET:
                    continue
                setattr(
                    self.flake8.options,
                    name,
                    option.normalize_from_setuptools(value),
                )

    def package_files(self):
        """Collect the files/dirs included in the registered modules."""
        seen_package_directories = ()
        directories = self.distribution.package_dir or {}
        empty_directory_exists = "" in directories
        packages = self.distribution.packages or []
        for package in packages:
            package_directory = package
            if package in directories:
                package_directory = directories[package]
            elif empty_directory_exists:
                package_directory = os.path.join(
                    directories[""], package_directory
                )

            # NOTE(sigmavirus24): Do not collect submodules, e.g.,
            # if we have:
            #  - flake8/
            #  - flake8/plugins/
            # Flake8 only needs ``flake8/`` to be provided. It will
            # recurse on its own.
            if package_directory.startswith(seen_package_directories):
                continue

            seen_package_directories += (package_directory + ".",)
            yield package_directory

    def module_files(self):
        """Collect the files listed as py_modules."""
        modules = self.distribution.py_modules or []
        filename_from = "{0}.py".format
        for module in modules:
            yield filename_from(module)

    def distribution_files(self):
        """Collect package and module files."""
        for package in self.package_files():
            yield package

        for module in self.module_files():
            yield module

        yield "setup.py"

    def run(self):
        """Run the Flake8 application."""
        self.flake8.run_checks(list(self.distribution_files()))
        self.flake8.formatter.start()
        self.flake8.report_errors()
        self.flake8.report_statistics()
        self.flake8.report_benchmarks()
        self.flake8.formatter.stop()
        try:
            self.flake8.exit()
        except SystemExit as e:
            # Cause system exit only if exit code is not zero (terminates
            # other possibly remaining/pending setuptools commands).
            if e.code:
                raise
