import os
import re
import threading
from datetime import datetime


class UseckitSessionFolder:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, folder_name: str = None):
        """Implements singleton pattern with thread safety."""
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, folder_name: str = None):
        # Ensure __init__ runs only once per singleton creation
        if hasattr(self, "_initialized") and self._initialized:
            return

        timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
        self.folder_name = folder_name or f"_useckit_{timestamp}"
        self.folder_base_path = os.path.join(os.getcwd(), self.folder_name)

        os.makedirs(self.folder_base_path, exist_ok=True)
        self._initialized = True

    @classmethod
    def get_instance(cls):
        """Access the existing singleton instance."""
        if cls._instance is None:
            raise RuntimeError("UseckitSessionFolder not created yet.")
        return cls._instance

    @classmethod
    def destroy_instance(cls):
        """Destroys the singleton instance (does not delete the folder)."""
        with cls._lock:
            cls._instance = None

    def __repr__(self):
        return f"<UseckitSessionFolder path={self.folder_base_path}>"

    def get_folder_basepath(self):
        return self.folder_base_path


class ExperimentFolder:
    def __init__(self, folder_description: str = None):
        _session = UseckitSessionFolder()
        _base_path = _session.get_folder_basepath()

        # Find all existing experiment folders 
        existing_folders = [
            name for name in os.listdir(_base_path)
            if os.path.isdir(os.path.join(_base_path, name)) and name.startswith("experiment_")
        ]

        # Extract numeric indices from folder names using regex
        numbers = []
        for name in existing_folders:
            match = re.match(r"experiment_(\d+)", name)
            if match:
                numbers.append(int(match.group(1)))

        next_index = (max(numbers) + 1) if numbers else 0

        # Construct new folder name
        if folder_description:
            safe_desc = re.sub(r"[^a-zA-Z0-9_-]", "_", folder_description.strip())
            folder_name = f"experiment_{next_index}_{safe_desc}"
        else:
            folder_name = f"experiment_{next_index}"

        self.folder_path = os.path.join(_base_path, folder_name)

        # Create the folder
        os.makedirs(self.folder_path, exist_ok=True)

    def get_path(self):
        return self.folder_path

    def __repr__(self):
        return f"<ExperimentFolder path={self.folder_path}>"
