from datetime import datetime, timezone
from pathlib import Path
import platform
from platformdirs import PlatformDirs
from typing import Optional, List
from uuid import UUID


DATE_FORMAT_FOR_FILE_NAME = "Y%Y_M%m_D%d_T%H_%M_%S_%f" # e.g. Y2025_M09_D23_T16_26_00_550339
DATE_FORMAT_DATE_ONLY = "Y%Y_M%m_D%d"               # e.g. Y2025_M09_D23

def get_forcen_tmp_root(create_if_not_exists: bool = True) -> Path:
    """Get the path to the root directory of all Forcen logs, as found by PlatformDirs.

    Args:
        create_if_not_exists (bool, optional): create the directory if not found. Defaults to True.

    Returns:
        Path: The path object to the newly created directory.
    """
    # This yields something like %AppData%/Local/Forcen/forcen_logging/Logs on Windows 10x64.
    platform_dirs_for_app = PlatformDirs(appname="", appauthor="Forcen")
    forcen_tmp_root = platform_dirs_for_app.user_log_path

    # On some platforms, the appauthor subdir may not appear. Inject it if its missing
    if platform.system().lower() in ["linux", "darwin", "windows"]:
        forcen_tmp_root = forcen_tmp_root.parents[0] / "Forcen"

    if create_if_not_exists and not forcen_tmp_root.exists():
        forcen_tmp_root.mkdir(parents=True, exist_ok=True)

    return forcen_tmp_root


def get_forcen_app_path(
    app_name: str,
    user_tmp_root: Optional[Path] = None,
    create_if_not_exists: bool = True,
) -> Path:
    """Get the path to the folder <user_tmp_root>/<app_name>/

    Args:
        app_name (str): The title of the app.
        user_tmp_root (path, optional): the root folder where the app is contained.
        create_if_not_exists (bool, optional): Create the folder if not found. Defaults to True.

    Returns:
        Path: A path object to the newly created directory.
    """
    tmp_root = (
        user_tmp_root if user_tmp_root else get_forcen_tmp_root(create_if_not_exists)
    )
    forcen_app_path = tmp_root / app_name

    if create_if_not_exists and not forcen_app_path.exists():
        forcen_app_path.mkdir(parents=True, exist_ok=True)

    return forcen_app_path


def get_forcen_app_log_path(
    app_name: str,
    user_tmp_root: Optional[Path] = None,
    create_if_not_exists: bool = True,
) -> Path:
    """Get the path to the folder <user_tmp_root>/<app_name>/logs/

    Args:
        app_name (str): The title of the folder for this app's logs.
        user_tmp_root (path, optional): the root folder where the app is contained.
        create_if_not_exists (bool, optional): Create the folder. Defaults to True.

    Returns:
        Path: A path object to the newly created directory.
    """
    forcen_app_path = get_forcen_app_path(app_name, user_tmp_root, create_if_not_exists)
    forcen_app_logs_path = forcen_app_path / "logs"

    if create_if_not_exists and not forcen_app_logs_path.exists():
        forcen_app_logs_path.mkdir(parents=True, exist_ok=True)

    return forcen_app_logs_path


def get_forcen_app_log_exec_instance_path(
    app_name: str,
    execution_date: datetime,
    execution_uuid: UUID,
    user_tmp_root: Optional[Path] = None,
    create_if_not_exists: bool = True,
) -> Path:
    """Get the path to the directory at <user_tmp_root>/<app_name>/logs/<datetime>_<uuid>/

    Args:
        app_name (str): The title of the app for this instance.
        execution_date (datetime): The datetime which describes the folder.
        execution_uuid (UUID): A unique identifier for this execution.
        user_tmp_root (path, optional): the root folder where the app is contained.
        create_if_not_exists (bool, optional): Create the directory if not found. Defaults to True.

    Returns:
        Path: The path the the directory
    """
    app_logging_root_path = get_forcen_app_log_path(
        app_name, user_tmp_root, create_if_not_exists
    )
    exec_uuid_str = execution_uuid.hex
    logging_datestamp = execution_date.strftime(DATE_FORMAT_FOR_FILE_NAME)
    exec_instance_folder_name = f"{logging_datestamp}_{exec_uuid_str}"
    forcen_app_exec_instance_log_path = (
        app_logging_root_path / exec_instance_folder_name
    )

    if create_if_not_exists and not forcen_app_exec_instance_log_path.exists():
        forcen_app_exec_instance_log_path.mkdir(parents=True, exist_ok=True)

    return forcen_app_exec_instance_log_path


def get_app_log_instances_from_uuid(
    app_name: str, execution_uuid: UUID, user_tmp_root: Optional[Path] = None
) -> List[Path]:
    """Get paths to the directories <user_tmp_root>/<app_name>/logs/*<uuid>/.

    Args:
        app_name (str): the app in which to search.
        execution_uuid (UUID): the unique identifier to find.
        user_tmp_root (Optional[Path], optional): the root folder where the app is contained.

    Returns:
        List[Path]: all subfolders of the app's logs which contain the given uuid in the name.
    """
    log_dir = get_forcen_app_log_path(
        app_name, user_tmp_root, create_if_not_exists=False
    )

    if not log_dir.exists():
        return list()

    all_uuid_files = log_dir.glob(f"*{execution_uuid.hex}")
    return [path for path in all_uuid_files if path.is_dir()]


def get_datetime_from_folder(folder: Path) -> datetime:
    """Convert a folder name to a datetime object.

    The folder must be named as <date>_<uuid>, where <date> meets one of the following date format strings:
        - "Y%Y_M%m_D%d_T%H_%M_%S_%f"
        - "Y%Y_M%m_D%d", or

    Args:
        folder_name (pathlib.Path): the path to the folder, which does not need to exist.

    Returns:
        datetime

    Raises:
        ValueError: if the folder name does not meet the format.
    """
    folder_name = folder.name

    # We expect the folder name to always end in "_<uuid>", where uuid does not have underscores
    # Take everything before the final underscore as folder name (the part before the UUID).
    if "_" not in folder_name:
        raise ValueError(f"Folder {folder} has no '_' to separate date and uuid.")

    date_segment = folder_name.rsplit("_", 1)[0]

    for fmt in (DATE_FORMAT_FOR_FILE_NAME, DATE_FORMAT_DATE_ONLY):
        try:
            dt = datetime.strptime(date_segment, fmt)
            # Normalize to UTC-aware for safe comparisons downstream
            return dt.replace(tzinfo=timezone.utc)
        except ValueError:
            continue

    raise ValueError(
        f"Folder {folder} does not meet allowable formats: "
        f"{[DATE_FORMAT_FOR_FILE_NAME, DATE_FORMAT_DATE_ONLY]}."
    )
