"""
Centralized path configuration for Local Deep Research.
Handles database location using platformdirs for proper user data storage.
"""

import hashlib
import os
from pathlib import Path

import platformdirs
from loguru import logger


def get_data_directory() -> Path:
    """
    Get the appropriate data directory for storing application data.
    Uses platformdirs to get platform-specific user data directory.

    Environment variable:
        LDR_DATA_DIR: Override the default data directory location.
                     All subdirectories (research_outputs, cache, logs, database)
                     will be created under this directory.

    Returns:
        Path to data directory
    """
    # Check for explicit override via environment variable
    custom_path = os.getenv("LDR_DATA_DIR")
    if custom_path:
        if not Path(custom_path).is_absolute():
            raise ValueError("LDR_DATA_DIR must be an absolute path")
        data_dir = Path(custom_path).resolve()
        # Reject only control characters that are never valid in a path and
        # could break a SQL/ATTACH statement (null byte, newline, carriage
        # return). Quotes are intentionally NOT rejected here: legitimate POSIX
        # home directories contain apostrophes (e.g. /home/O'Brien/ldr), and the
        # actual ATTACH-DATABASE sink (backup_service) already validates the
        # full interpolated path against a stricter denylist before use.
        # The offending path is not echoed, mirroring the redaction below.
        path_str = str(data_dir)
        if any(c in path_str for c in ("\0", "\n", "\r")):
            raise ValueError(
                "LDR_DATA_DIR contains unsafe control characters "
                "(null byte, newline, or carriage return)"
            )
        logger.debug(
            f"Using custom data directory from LDR_DATA_DIR: {data_dir}"
        )
        return data_dir

    # Use platformdirs for platform-specific user data directory
    # Windows: C:\Users\Username\AppData\Local\local-deep-research
    # macOS: ~/Library/Application Support/local-deep-research
    # Linux: ~/.local/share/local-deep-research
    data_dir = Path(platformdirs.user_data_dir("local-deep-research"))
    # Log only the directory pattern, not the full path which may contain username
    logger.debug(
        f"Using platformdirs data directory pattern: .../{data_dir.name}"
    )

    return data_dir


def _ensure_dir(subdir: str, label: str | None = None) -> Path:
    """Create (if needed) and return ``<data_dir>/<subdir>``.

    Args:
        subdir: Path segment below the data directory. May be multi-segment
            (e.g. ``"encrypted_databases/backups"``).
        label: If provided, emit a debug log ``"Using {label} directory:
            {path}"``. ``None`` suppresses logging.

    Returns:
        The ensured directory path.
    """
    path = get_data_directory() / subdir
    path.mkdir(parents=True, exist_ok=True)
    if label is not None:
        logger.debug(f"Using {label} directory: {path}")
    return path


def get_research_outputs_directory() -> Path:
    """
    Get the directory for storing research outputs (reports, etc.).

    Returns:
        Path to research outputs directory
    """
    return _ensure_dir("research_outputs", label="research outputs")


def get_journal_data_directory() -> Path:
    """Get the directory for downloaded journal quality data files.

    Contains openalex_sources.json.gz, doaj_journals.json, and the
    compiled journal_reference.db. Fetched on first use from
    OpenAlex and DOAJ APIs.

    Returns:
        Path to journal data directory
    """
    return _ensure_dir("journal_data")


def get_cache_directory() -> Path:
    """
    Get the directory for storing cache files (search cache, etc.).

    Returns:
        Path to cache directory
    """
    return _ensure_dir("cache", label="cache")


def get_logs_directory() -> Path:
    """
    Get the directory for storing log files.

    Returns:
        Path to logs directory
    """
    return _ensure_dir("logs", label="logs")


def get_encrypted_database_path() -> Path:
    """Get the path to the encrypted databases directory.

    Returns:
        Path to the encrypted databases directory
    """
    return _ensure_dir("encrypted_databases")


def get_user_database_filename(username: str) -> str:
    """Get the database filename for a specific user.

    Args:
        username: The username to generate a filename for

    Returns:
        The database filename (not full path) for the user
    """
    # Use username hash to avoid filesystem issues with special characters
    username_hash = hashlib.sha256(username.encode()).hexdigest()[:16]
    return f"ldr_user_{username_hash}.db"


def get_library_directory() -> Path:
    """
    Get the directory for storing library files (documents, PDFs, etc.).

    Returns:
        Path to library directory
    """
    return _ensure_dir("library", label="library")


def get_config_directory() -> Path:
    """
    Get the directory for storing configuration files.

    Returns:
        Path to config directory
    """
    return _ensure_dir("config", label="config")


def get_models_directory() -> Path:
    """
    Get the directory for storing downloaded models.

    Returns:
        Path to models directory
    """
    return _ensure_dir("models", label="models")


def get_backup_directory() -> Path:
    """Get the base backup directory for all users."""
    return _ensure_dir("encrypted_databases/backups")


def get_user_backup_directory(username: str) -> Path:
    """Get backup directory for a specific user."""
    username_hash = hashlib.sha256(username.encode()).hexdigest()[:16]
    user_backup_dir = get_backup_directory() / username_hash
    user_backup_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
    # Enforce 0o700 regardless of umask (mkdir mode is umask-masked)
    user_backup_dir.chmod(0o700)
    return user_backup_dir


# Convenience functions for backward compatibility
def get_data_dir() -> str:
    """Get data directory as string for backward compatibility."""
    return str(get_data_directory())
