"""Support for generating an icon file representing a node type in the build directory."""
import os
import re
import shutil
from typing import Optional

from .nodes import NodeInterfaceGenerator
from .utils import GeneratorConfiguration, ParseError, ensure_writable_directory, logger

__all__ = ["generate_icon"]


class NodeIconGenerator(NodeInterfaceGenerator):
    """Manage the functions required to install a representative icon for a node type"""

    def __init__(self, configuration: GeneratorConfiguration):  # noqa: PLW0246
        """Set up the generator to get ready to copy the icon file

        Just passes the initialization on to the parent class. See the argument and exception descriptions there.
        """
        super().__init__(configuration)

    # ----------------------------------------------------------------------
    def interface_file_name(self) -> str:
        """Return the path to the name of the node icon file"""
        return f"{self.node_interface.name}.svg"

    # ----------------------------------------------------------------------
    def generate_node_interface(self):
        """Generate the test method for the named node"""
        # If there is no place to put the file then the specified path, if it exists, is considered absolute
        if self.output_path is None:
            self.out.write(self.node_interface.icon_path)
            return

        # This ensures the file is not overwritten by the base class after returning
        output_path = self.output_path
        self.output_path = None

        potential_path = self.node_interface.icon_path
        if self.node_file_path is not None:
            # If a file name was not explicit then see if a .svg file with the same name as the node exists
            if potential_path is None:
                # If no file was specified it's not an error if it doesn't exist
                potential_path = self.node_file_path.replace(".ogn", ".svg")
                if not os.path.isfile(potential_path):
                    return
            else:
                # The specified file name is relative to the node's directory; make it absolute
                potential_path = os.path.join(os.path.dirname(self.node_file_path), potential_path)
                if not potential_path.endswith(".svg"):
                    raise ParseError(f"Node icon path must be an SVG file. '{potential_path}' not allowed")

        # Copy the file from the source location to the output path
        try:
            ensure_writable_directory(os.path.dirname(output_path))
            shutil.copy(potential_path, output_path)
        except Exception as error:
            raise ParseError("Failed to copy node icon file") from error

        # Find the icon path relative to the extension directory
        match = re.match(f".*/{self.extension}/(.*)", output_path.replace("\\", "/"))
        if not match:
            raise ParseError(f"Icon location '{output_path}' needs to appear under extension '{self.extension}'")
        extension_relative_path = match.group(1)

        self.out.write(extension_relative_path)


# ======================================================================
def generate_icon(configuration: GeneratorConfiguration) -> Optional[str]:
    """Create support files for the icons defined within the node

    Args:
        configuration: Information defining how and where the documentation will be generated

    Returns:
        Relative path of the icon file if it existed and was successfully installed, else None

    Raises:
        NodeGenerationError: When there is a failure in the generation of the icon files
    """
    if not configuration.node_interface.can_generate("icon"):
        return None
    logger.info("Generating icon")
    needed_directory = configuration.needs_directory

    configuration.needs_directory = False
    generator = NodeIconGenerator(configuration)
    generator.generate_interface()

    configuration.needs_directory = needed_directory

    icon_path = str(generator.out).rstrip()
    return icon_path if icon_path else None
