from debputy.plugin.api.test_api import (
    initialize_plugin_under_test_preloaded,
    build_virtual_file_system,
    package_metadata_context,
    virtual_path,
)
from debputy.plugin.debputy.debputy_plugin import initialize_debputy_features


def test_debputy_packager_provided_files():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    ppf_by_stem = plugin.packager_provided_files_by_stem()
    # Verify that all the files are loaded
    assert set(ppf_by_stem.keys()) == {
        "tmpfiles",
        "sysusers",
        "pam",
        "ppp.ip-up",
        "ppp.ip-down",
        "logrotate",
        "logcheck.cracking",
        "logcheck.violations",
        "logcheck.violations.ignore",
        "logcheck.ignore.workstation",
        "logcheck.ignore.server",
        "logcheck.ignore.paranoid",
        "mime",
        "sharedmimeinfo",
        "if-pre-up",
        "if-up",
        "if-down",
        "if-post-down",
        "cron.hourly",
        "cron.daily",
        "cron.weekly",
        "cron.monthly",
        "cron.yearly",
        "cron.d",
        "initramfs-hook",
        "modprobe",
        "udev",
        "gsettings-override",
        "lintian-overrides",
        "bug-script",
        "bug-control",
        "bug-presubj",
        "changelog",
        "NEWS",
        "copyright",
        "README.Debian",
        "TODO",
        "doc-base",
    }
    # Verify the post_rewrite_hook
    assert (
        ppf_by_stem["logcheck.ignore.paranoid"].compute_dest_basename("foo.bar")
        == "foo_bar"
    )
    # Verify custom formats work
    assert ppf_by_stem["tmpfiles"].compute_dest_basename("foo.bar") == "foo.bar.conf"
    assert (
        ppf_by_stem["sharedmimeinfo"].compute_dest_basename("foo.bar") == "foo.bar.xml"
    )
    assert ppf_by_stem["modprobe"].compute_dest_basename("foo.bar") == "foo.bar.conf"
    assert (
        ppf_by_stem["gsettings-override"].compute_dest_basename(
            "foo.bar", assigned_priority=20
        )
        == "20_foo.bar.gschema.override"
    )


def test_debputy_metadata_detector_gesttings_dependencies():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    # By default, the plugin will not add a substvars
    fs_root = build_virtual_file_system(["./bin/ls"])
    plugin.run_metadata_detector("gsettings-dependencies", fs_root)
    assert "misc:Depends" not in plugin.substvars

    # It will not react if there is only directories or non-files
    fs_root = build_virtual_file_system(["./usr/share/glib-2.0/schemas/some-dir/"])
    plugin.run_metadata_detector("gsettings-dependencies", fs_root)
    assert "misc:Depends" not in plugin.substvars

    # However, it will if there is a file beneath the schemas dir
    fs_root = build_virtual_file_system(["./usr/share/glib-2.0/schemas/foo.xml"])
    plugin.run_metadata_detector("gsettings-dependencies", fs_root)
    assert (
        plugin.substvars["misc:Depends"]
        == "dconf-gsettings-backend | gsettings-backend"
    )


def test_debputy_metadata_detector_initramfs_hooks():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "initramfs-hooks"

    # By default, the plugin will not add a trigger
    fs_root = build_virtual_file_system(["./bin/ls"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.trigger_provided_by(metadata_detector_id) == []

    # It will not react if the directory is empty
    fs_root = build_virtual_file_system(
        [
            # Use an absolute path to verify that also work (it should and third-party plugin are likely
            # use absolute paths)
            "/usr/share/initramfs-tools/hooks/"
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.trigger_provided_by(metadata_detector_id) == []

    # However, it will if there is a file beneath the schemas dir
    fs_root = build_virtual_file_system(["./usr/share/initramfs-tools/hooks/some-hook"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    result = [
        t.serialized_format() for t in plugin.trigger_provided_by(metadata_detector_id)
    ]
    assert result == ["activate-noawait update-initramfs"]


def test_debputy_metadata_detector_systemd_tmpfiles():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "systemd-tmpfiles"

    # By default, the plugin will not add anything
    fs_root = build_virtual_file_system(["./bin/ls"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    # It only reacts to ".conf" files
    fs_root = build_virtual_file_system(["./usr/lib/tmpfiles.d/foo"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    fs_root = build_virtual_file_system(
        [
            "./usr/lib/tmpfiles.d/foo.conf",
            "./etc/tmpfiles.d/foo.conf",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    snippets = plugin.maintscript_registered_by(metadata_detector_id)
    assert len(snippets) == 1
    snippet = snippets[0]
    assert snippet.maintscript == "postinst"
    assert snippet.registration_method == "on_configure"
    # The snippet should use "systemd-tmpfiles [...] --create foo.conf ..."
    assert "--create foo.conf" in snippet.detector_provided_script
    # The "foo.conf" should only be listed once
    assert snippet.detector_provided_script.count("foo.conf") == 1


def test_debputy_metadata_detector_systemd_sysusers():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "systemd-sysusers"

    # By default, the plugin will not add anything
    fs_root = build_virtual_file_system(["./bin/ls"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    # It only reacts to ".conf" files
    fs_root = build_virtual_file_system(["./usr/lib/sysusers.d/foo"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    fs_root = build_virtual_file_system(["./usr/lib/sysusers.d/foo.conf"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    snippets = plugin.maintscript_registered_by(metadata_detector_id)
    assert len(snippets) == 1
    snippet = snippets[0]
    assert snippet.maintscript == "postinst"
    assert snippet.registration_method == "on_configure"
    # The snippet should use "systemd-sysusers [...] foo.conf ..."
    assert "systemd-sysusers" in snippet.detector_provided_script
    assert "foo.conf" in snippet.detector_provided_script
    # The "foo.conf" should only be listed once
    assert snippet.detector_provided_script.count("foo.conf") == 1


def test_debputy_metadata_detector_xfonts():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "xfonts"

    # By default, the plugin will not add anything
    fs_root = build_virtual_file_system(["./bin/ls"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []
    assert "misc:Depends" not in plugin.substvars

    # It ignores files in the X11 dir and directories starting with ".".
    fs_root = build_virtual_file_system(
        ["./usr/share/fonts/X11/foo", "./usr/share/fonts/X11/.a/"]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []
    assert "misc:Depends" not in plugin.substvars

    fs_root = build_virtual_file_system(
        [
            "./usr/share/fonts/X11/some-font-dir/",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    snippets = plugin.maintscript_registered_by(metadata_detector_id)
    assert plugin.substvars["misc:Depends"] == "xfonts-utils"
    assert len(snippets) == 2
    assert set(s.maintscript for s in snippets) == {"postinst", "postrm"}
    postinst_snippet = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postinst"
    )[0]
    postrm_snippet = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postrm"
    )[0]

    assert postinst_snippet.maintscript == "postinst"
    assert postinst_snippet.registration_method == "unconditionally_in_script"
    assert (
        "update-fonts-scale some-font-dir"
        not in postinst_snippet.detector_provided_script
    )
    assert "--x11r7-layout some-font-dir" in postinst_snippet.detector_provided_script
    assert (
        f"update-fonts-alias --include" not in postinst_snippet.detector_provided_script
    )

    assert postrm_snippet.maintscript == "postrm"
    assert postrm_snippet.registration_method == "unconditionally_in_script"
    assert (
        "update-fonts-scale some-font-dir"
        not in postrm_snippet.detector_provided_script
    )
    assert "--x11r7-layout some-font-dir" in postrm_snippet.detector_provided_script
    assert (
        f"update-fonts-alias --exclude" not in postrm_snippet.detector_provided_script
    )


def test_debputy_metadata_detector_xfonts_scale_and_alias():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )

    metadata_detector_id = "xfonts"
    package_name = "bar"
    fs_root = build_virtual_file_system(
        [
            "./usr/share/fonts/X11/some-font-dir/",
            f"./etc/X11/xfonts/some-font-dir/{package_name}.scale",
            f"./etc/X11/xfonts/some-font-dir/{package_name}.alias",
        ]
    )
    plugin.run_metadata_detector(
        metadata_detector_id,
        fs_root,
        package_metadata_context(
            package_fields={
                "Package": package_name,
            }
        ),
    )
    snippets = plugin.maintscript_registered_by(metadata_detector_id)
    assert plugin.substvars["misc:Depends"] == "xfonts-utils"
    assert len(snippets) == 2
    assert set(s.maintscript for s in snippets) == {"postinst", "postrm"}
    postinst_snippet = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postinst"
    )[0]
    postrm_snippet = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postrm"
    )[0]

    assert postinst_snippet.maintscript == "postinst"
    assert postinst_snippet.registration_method == "unconditionally_in_script"
    assert (
        "update-fonts-scale some-font-dir" in postinst_snippet.detector_provided_script
    )
    assert "--x11r7-layout some-font-dir" in postinst_snippet.detector_provided_script
    assert (
        f"update-fonts-alias --include /etc/X11/xfonts/some-font-dir/{package_name}.alias some-font-dir"
        in postinst_snippet.detector_provided_script
    )

    assert postrm_snippet.maintscript == "postrm"
    assert postrm_snippet.registration_method == "unconditionally_in_script"
    assert "update-fonts-scale some-font-dir" in postrm_snippet.detector_provided_script
    assert "--x11r7-layout some-font-dir" in postrm_snippet.detector_provided_script
    assert (
        f"update-fonts-alias --exclude /etc/X11/xfonts/some-font-dir/{package_name}.alias some-font-dir"
        in postrm_snippet.detector_provided_script
    )


def test_debputy_metadata_detector_icon_cache():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "icon-cache"
    icon_dir = "usr/share/icons"

    # By default, the plugin will not add anything
    fs_root = build_virtual_file_system(["./bin/ls"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    fs_root = build_virtual_file_system(
        [
            # Ignored subdirs (dh_icons ignores them too)
            f"./{icon_dir}/gnome/foo.png",
            f"./{icon_dir}/hicolor/foo.png",
            # Unknown image format, so it does not trigger the update-icon-caches call
            f"./{icon_dir}/subdir-a/unknown-image-format.img",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    fs_root = build_virtual_file_system(
        [
            f"./{icon_dir}/subdir-a/foo.png",
            f"./{icon_dir}/subdir-b/subsubdir/bar.svg",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    snippets = plugin.maintscript_registered_by(metadata_detector_id)
    assert len(snippets) == 2
    assert set(s.maintscript for s in snippets) == {"postinst", "postrm"}
    postinst_snippet = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postinst"
    )[0]
    postrm_snippet = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postrm"
    )[0]

    assert postinst_snippet.registration_method == "on_configure"
    assert postrm_snippet.registration_method == "unconditionally_in_script"

    # Directory order is stable according to the BinaryPackagePath API.
    assert (
        f"update-icon-caches /{icon_dir}/subdir-a /{icon_dir}/subdir-b"
        in postinst_snippet.detector_provided_script
    )
    assert (
        f"update-icon-caches /{icon_dir}/subdir-a /{icon_dir}/subdir-b"
        in postrm_snippet.detector_provided_script
    )


def test_debputy_metadata_detector_kernel_modules():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "kernel-modules"
    module_dir = "lib/modules"

    # By default, the plugin will not add anything
    fs_root = build_virtual_file_system(["./bin/ls"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    fs_root = build_virtual_file_system(
        [
            # Ignore files directly in the path or with wrong extension
            f"./{module_dir}/README",
            f"./{module_dir}/3.11/ignored-file.txt",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    fs_root = build_virtual_file_system(
        [
            f"./{module_dir}/3.11/foo.ko",
            f"./usr/{module_dir}/3.12/bar.ko.xz",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    snippets = plugin.maintscript_registered_by(metadata_detector_id)
    assert len(snippets) == 4  # Two for each version
    assert set(s.maintscript for s in snippets) == {"postinst", "postrm"}
    postinst_snippets = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postinst"
    )
    postrm_snippets = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postrm"
    )

    assert len(postinst_snippets) == 2
    assert len(postrm_snippets) == 2
    assert {s.registration_method for s in postinst_snippets} == {"on_configure"}
    assert {s.registration_method for s in postrm_snippets} == {
        "unconditionally_in_script"
    }

    assert (
        "depmod -a -F /boot/System.map-3.11 3.11"
        in postinst_snippets[0].detector_provided_script
    )
    assert (
        "depmod -a -F /boot/System.map-3.12 3.12"
        in postinst_snippets[1].detector_provided_script
    )

    assert (
        "depmod -a -F /boot/System.map-3.11 3.11"
        in postrm_snippets[0].detector_provided_script
    )
    assert (
        "depmod -a -F /boot/System.map-3.12 3.12"
        in postrm_snippets[1].detector_provided_script
    )


def test_debputy_metadata_detector_dpkg_shlibdeps():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "dpkg-shlibdeps"
    skip_root_dir = "usr/lib/debug/"

    # By default, the plugin will not add anything
    fs_root = build_virtual_file_system(
        [
            "./usr/share/doc/foo/copyright",
            virtual_path("./usr/lib/debuty/test.py", fs_path=__file__),
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert "shlibs:Depends" not in plugin.substvars

    fs_root = build_virtual_file_system(
        [
            # Verify that certain directories are skipped as promised
            virtual_path(f"./{skip_root_dir}/bin/ls", fs_path="/bin/ls")
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert "shlibs:Depends" not in plugin.substvars

    # But we detect ELF binaries elsewhere
    fs_root = build_virtual_file_system([virtual_path(f"./bin/ls", fs_path="/bin/ls")])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    # Do not make assertions about the content of `shlibs:Depends` as
    # package name and versions change over time.
    assert "shlibs:Depends" in plugin.substvars

    del plugin.substvars["shlibs:Depends"]
    assert "shlibs:Depends" not in plugin.substvars

    # Re-run to verify it runs for udebs as well
    plugin.run_metadata_detector(
        metadata_detector_id,
        fs_root,
        context=package_metadata_context(
            package_fields={"Package-Type": "udeb"},
        ),
    )
    assert "shlibs:Depends" in plugin.substvars


def test_debputy_metadata_detector_pycompile_files():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "pycompile-files"
    module_dir = "usr/lib/python3/dist-packages"

    # By default, the plugin will not add anything
    fs_root = build_virtual_file_system(["./bin/ls"])
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    fs_root = build_virtual_file_system(
        [
            # Ignore files in unknown directories by default
            "./random-dir/foo.py",
            # Must be in "dist-packages" to count
            "./usr/lib/python3/foo.py",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    assert plugin.maintscript_registered_by(metadata_detector_id) == []

    fs_root = build_virtual_file_system(
        [
            f"./{module_dir}/debputy/foo.py",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    snippets = plugin.maintscript_registered_by(metadata_detector_id)
    assert len(snippets) == 2
    assert set(s.maintscript for s in snippets) == {"postinst", "prerm"}
    postinst_snippets = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postinst"
    )
    prerm_snippets = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="prerm"
    )

    assert len(postinst_snippets) == 1
    assert len(prerm_snippets) == 1
    assert {s.registration_method for s in postinst_snippets} == {"on_configure"}
    assert {s.registration_method for s in prerm_snippets} == {
        "unconditionally_in_script"
    }

    assert "py3compile -p foo" in postinst_snippets[0].detector_provided_script

    assert "py3clean -p foo" in prerm_snippets[0].detector_provided_script


def test_debputy_metadata_detector_pycompile_files_private_package_dir():
    plugin = initialize_plugin_under_test_preloaded(
        1,
        initialize_debputy_features,
        plugin_name="debputy",
        load_debputy_plugin=False,
    )
    metadata_detector_id = "pycompile-files"
    module_dir = "usr/share/foo"

    fs_root = build_virtual_file_system(
        [
            f"./{module_dir}/debputy/foo.py",
        ]
    )
    plugin.run_metadata_detector(metadata_detector_id, fs_root)
    snippets = plugin.maintscript_registered_by(metadata_detector_id)
    assert len(snippets) == 2
    assert set(s.maintscript for s in snippets) == {"postinst", "prerm"}
    postinst_snippets = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="postinst"
    )
    prerm_snippets = plugin.maintscript_registered_by(
        metadata_detector_id, maintscript="prerm"
    )

    assert len(postinst_snippets) == 1
    assert len(prerm_snippets) == 1
    assert {s.registration_method for s in postinst_snippets} == {"on_configure"}
    assert {s.registration_method for s in prerm_snippets} == {
        "unconditionally_in_script"
    }

    assert (
        f"py3compile -p foo /{module_dir}"
        in postinst_snippets[0].detector_provided_script
    )

    assert "py3clean -p foo" in prerm_snippets[0].detector_provided_script
