"""Adds a `source_path` attribute to imported albums indicating from what path
the album was imported from. Also suggests removing that source path in case
you've removed the album from the library.

"""

from __future__ import annotations

import os
from pathlib import Path
from shutil import rmtree
from typing import TYPE_CHECKING

from beets.dbcore.query import PathQuery
from beets.plugins import BeetsPlugin
from beets.ui import input_options
from beets.util.color import colorize

if TYPE_CHECKING:
    from beets.importer import ImportSession, ImportTask
    from beets.library import Item


class ImportSourcePlugin(BeetsPlugin):
    """Main plugin class."""

    stop_suggestions_for_albums: set[str]

    def __init__(self) -> None:
        """Initialize the plugin and read configuration."""
        super().__init__()
        self.config.add({"suggest_removal": False})
        self.import_stages = [self.import_stage]
        # In order to stop future removal suggestions for an album we keep
        # track of `mb_albumid`s in this set. Always initialize to avoid
        # AttributeError when methods access this even if feature disabled.
        self.stop_suggestions_for_albums = set()
        # Only register removal suggestion listeners if the feature is enabled
        if self.config["suggest_removal"]:
            self.register_listener("item_removed", self.suggest_removal)
            # During reimports (import --library) both the import_task_choice and
            # the item_removed event are triggered. The item_removed event is
            # triggered first. For the import_task_choice event we prevent removal
            # suggestions using the existing stop_suggestions_for_album mechanism.
            self.register_listener(
                "import_task_choice", self.prevent_suggest_removal
            )

    def prevent_suggest_removal(
        self, session: ImportSession, task: ImportTask
    ) -> None:
        if task.skip:
            return
        for item in task.imported_items():
            if "mb_albumid" in item:
                self.stop_suggestions_for_albums.add(item.mb_albumid)

    def import_stage(self, _, task: ImportTask) -> None:
        """Event handler for albums import finished."""
        for item in task.imported_items():
            # During reimports (import --library), we prevent overwriting the
            # source_path attribute with the path from the music library
            if "source_path" in item:
                self._log.info(
                    "Preserving source_path of reimported item {}", item.id
                )
                continue
            item["source_path"] = item.path
            item.try_sync(write=False, move=False)

    def suggest_removal(self, item: Item) -> None:
        """Prompts the user to delete the original path the item was imported from."""
        if item.mb_albumid in self.stop_suggestions_for_albums:
            return

        if "source_path" not in item:
            self._log.warning(
                "Item without source_path (probably imported before plugin "
                "usage): {}",
                item.filepath,
            )
            return

        srcpath = Path(os.fsdecode(item.source_path))
        if not srcpath.is_file():
            self._log.warning(
                "Original source file no longer exists or is not accessible: {}",
                srcpath,
            )
            return

        if not (
            os.access(srcpath, os.W_OK)
            and os.access(srcpath.parent, os.W_OK | os.X_OK)
        ):
            self._log.warning(
                "Original source file cannot be deleted (insufficient permissions): {}",
                srcpath,
            )
            return

        # We ask the user whether they'd like to delete the item's source
        # directory
        item_path = colorize("text_warning", item.filepath)
        source_path = colorize("text_warning", srcpath)

        print(
            f"The item:\n{item_path}\nis originated from:\n{source_path}\n"
            "What would you like to do?"
        )

        resp = input_options(
            [
                "Delete the item's source",
                "Recursively delete the source's directory",
                "do Nothing",
                "do nothing and Stop suggesting to delete items from this album",
            ],
            require=True,
        )

        # Handle user response
        if resp == "d":
            self._log.info("Deleting the item's source file: {}", srcpath)
            srcpath.unlink()

        elif resp == "r":
            self._log.info(
                "Searching for other items with a source_path attr containing: {}",
                srcpath.parent,
            )

            source_dir_query = PathQuery(
                "source_path",
                os.fsencode(srcpath.parent),
                # The "source_path" attribute may not be present in all
                # items of the library, so we avoid errors with this:
                fast=False,
            )

            print("Doing so will delete the following items' sources as well:")
            for searched_item in item.db.items(source_dir_query):
                print(colorize("text_warning", searched_item.filepath))

            print("Would you like to continue?")
            continue_resp = input_options(
                ["Yes", "delete None", "delete just the File"],
                require=False,  # Yes is the a default
            )

            if continue_resp == "y":
                self._log.info(
                    "Deleting the item's source directory: {}", srcpath.parent
                )
                rmtree(srcpath.parent)

            elif continue_resp == "n":
                self._log.info("doing nothing - aborting hook function")
                return

            elif continue_resp == "f":
                self._log.info(
                    "removing just the item's original source: {}", srcpath
                )
                srcpath.unlink()

        elif resp == "s":
            self.stop_suggestions_for_albums.add(item.mb_albumid)

        else:
            self._log.info("Doing nothing")
