import os
import shutil
from collections import defaultdict
from typing import Callable, Optional, Iterable

from setuptools import setup, find_packages, Extension
from setuptools.command.build_ext import build_ext

from dev_tools.picoture.script import build_picoture

HERE = os.path.abspath(os.path.dirname(__file__))
VERSION = "1.9.4"

with open(os.path.join(HERE, "README.md"), encoding="utf-8") as f:
    long_description = f.read()

requirements = defaultdict(list)
for name in os.listdir(os.path.join(HERE, "requirements")):
    if name not in ("base.in", "pkg.in", "test.in", "gui.in"):
        continue
    reqs = requirements[name.rpartition(".")[0]]
    with open(os.path.join(HERE, "requirements", name)) as f:
        for line in f:
            line = line.strip()
            if line.startswith("#") or line.startswith("-r"):
                continue
            reqs.append(line)
install_requirements = requirements.pop("base")


def _package_data_files(pkg: str, filter_fn: Optional[Callable[[str], bool]] = None) -> Iterable[str]:
    root = os.path.join(HERE, pkg)
    for parent, _, files in os.walk(root):
        for file in files:
            if file.endswith(".py") or file.endswith(".pyc"):
                continue
            rel_path = os.path.relpath(os.path.join(parent, file), root)
            if filter_fn is None or filter_fn(rel_path):
                yield rel_path


def _clarabot_nano_data_files_filter(rel_path: str) -> bool:
    if rel_path.endswith(".pot") or rel_path.endswith(".po"):
        return False
    if rel_path.startswith("picoture/"):
        _, _, picoture_path = rel_path.partition("/")
        if picoture_path.startswith(".") or picoture_path.startswith("build/") or picoture_path.startswith("dist/"):
            return False
    return True


package_data = {
    "clarabot_nano": sorted(_package_data_files("clarabot_nano", _clarabot_nano_data_files_filter)),
    # FIXME: Include the test suite, once it is possible to run it outside of the internal development system.
}
if package_data != {
    "clarabot_nano": [
        "picoture/build.gradle",
        "picoture/gradle.lockfile",
        "picoture/gradle/wrapper/gradle-wrapper.jar",
        "picoture/gradle/wrapper/gradle-wrapper.properties",
        "picoture/gradlew",
        "picoture/gradlew.bat",
        "picoture/settings.gradle",
        "picoture/src/main/java/com/clarabot/picoture/Picoture.java",
        "resource/image/clarabot.ico",
        "resource/image/clarabot_nano.icns",
        "resource/image/clarabot_nano.ico",
        "resource/image/clarabot_nano_256.png",
        "resource/image/wiz.png",
        "resource/locale/hu/LC_MESSAGES/clarabot-nano.mo",
        "resource/spawn/picoture/log4j2.xml",
        "resource/spawn/picoture/security.policy",
        "resource/spawn/solr/security.policy",
        "resource/spawn/tika/log4j.properties",
        "resource/spawn/tika/log4j2.xml",
        "resource/spawn/tika/security.policy",
        "resource/template/document/empty/calc.ots",
        "resource/template/document/empty/impress.otp",
        "resource/template/document/empty/writer.ott",
        "resource/template/local.yml",
        "resource/template/remote.yml",
        "resource/template/solr/configsets/nano/conf/managed-schema",
        "resource/template/solr/configsets/nano/conf/solrconfig.xml",
        "resource/template/solr/configsets/nano/lang/en/snippet.xml",
        "resource/template/solr/configsets/nano/lang/hu/snippet.xml",
        "resource/template/solr/solr.xml",
    ]
}:
    raise Exception(f"Package data has changed\n{str(package_data)}")


# Manually cleaning before build is required.
for p in [os.path.join(HERE, "build"), os.path.join(HERE, "dist"), os.path.join(HERE, "clarabot_nano.egg-info")]:
    if os.path.exists(p):
        shutil.rmtree(p)


class BuildExt(build_ext):
    def build_extension(self, ext):
        if ext.name == "clarabot_nano/picoture":
            inplace_picoture_dist_dir = build_picoture(force=True).parent
            ext_path = os.path.join(os.path.dirname(self.get_ext_fullpath(ext.name)), "picoture", "dist")
            shutil.copytree(inplace_picoture_dist_dir, ext_path)
        else:
            return build_ext.build_extension(self, ext)

    def copy_extensions_to_source(self):
        other_extensions = []
        for ext in self.extensions:
            if ext.name == "clarabot_nano/picoture":
                pass  # builds inplace already
            else:
                other_extensions.append(ext)
        # Call super implementation with hackily excluding the special extensions.
        all_extensions, self.extensions = self.extensions, other_extensions
        try:
            return build_ext.copy_extensions_to_source(self)
        finally:
            self.extensions = all_extensions


setup(
    name="clarabot-nano",
    version=VERSION,
    description="Clarabot Nano client application",
    long_description=long_description,
    url="clarabot.com",
    author="Nándor Mátravölgyi",
    author_email="nmatravolgyi@clarabot.com",
    # license="",
    classifiers=["License :: Other/Proprietary License"],
    packages=[p for p in find_packages(where=HERE, include=["clarabot_nano*"]) if not p.startswith("clarabot_nano_")],
    package_data=package_data,
    ext_modules=[Extension("clarabot_nano/picoture", sources=[])],
    install_requires=install_requirements,
    # entry_points={"console_scripts": console_scripts},
    extras_require=dict(requirements),
    python_requires=">=3.11",
    cmdclass={"build_ext": BuildExt},
)
