from abc import ABC, abstractmethod
from pathlib import Path

from jupyter_deploy.engine.engine_variables import EngineVariablesHandler
from jupyter_deploy.engine.enum import EngineType
from jupyter_deploy.engine.supervised_execution import CompletionContext
from jupyter_deploy.engine.vardefs import TemplateVariableDefinition
from jupyter_deploy.handlers.command_history_handler import CommandHistoryHandler
from jupyter_deploy.manifest import JupyterDeployManifest


class ReadConfigurationError(RuntimeError):
    """Raised when reading or parsing configuration fails."""

    def __init__(self, plan_path: str) -> None:
        self.plan_path = plan_path
        super().__init__(f"Failed to read or parse plan at: {plan_path}")


class WriteConfigurationError(RuntimeError):
    """Raised when writing configuration fails."""

    def __init__(self, file_path: str) -> None:
        self.file_path = file_path
        super().__init__(f"Failed to write configuration to: {file_path}")


class EngineConfigHandler(ABC):
    def __init__(
        self,
        project_path: Path,
        project_manifest: JupyterDeployManifest,
        engine: EngineType,
        variables_handler: EngineVariablesHandler,
        command_history_handler: CommandHistoryHandler,
    ) -> None:
        """Instantiate the base handler for `jd config` command."""
        self.project_path = project_path
        self.project_manifest = project_manifest
        self.engine = engine
        self.variables_handler = variables_handler
        self.command_history_handler = command_history_handler

    @abstractmethod
    def has_recorded_variables(self) -> bool:
        """Return True if the file generated by jd config to record variable values exists in the dir."""
        pass

    @abstractmethod
    def verify_preset_exists(self, preset_name: str) -> bool:
        """Return True if the requested preset is defined by this template."""
        pass

    @abstractmethod
    def list_presets(self) -> list[str]:
        """Return the list of default presets that this template supports."""
        pass

    @abstractmethod
    def configure(
        self, preset_name: str | None = None, variable_overrides: dict[str, TemplateVariableDefinition] | None = None
    ) -> CompletionContext | None:
        """Set the inputs, return a CompletionContext, or None if not available."""
        pass

    @abstractmethod
    def reset_recorded_variables(self) -> None:
        """Delete the file in the project dir where the previous inputs were recorded."""
        pass

    @abstractmethod
    def reset_recorded_secrets(self) -> None:
        """Delete the file in the project dir where the previous secrets were recorded."""
        pass

    @abstractmethod
    def record(self, record_vars: bool = False, record_secrets: bool = False) -> None:
        """Save the values of the variables and/or secrets to disk in the project dir."""
        pass
