# Copyright © The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Common utilities for Django-based packages."""

import os
import shutil
import sys
import tempfile
from pathlib import Path
from typing import Any

from django.conf import settings


def in_test_suite() -> bool:
    """Check whether we're running in the test suite."""
    # `"PYTEST_VERSION" in os.environ` would be a cleaner and simpler way to
    # detect whether pytest is running, but that requires pytest >= 8.2.
    return (
        sys.argv[1:2] == ["test"]
        or "pytest" in sys.argv[0].split(os.sep)
        or "PYTEST_XDIST_WORKER" in os.environ
    )


def setup_temp_data_directories() -> None:
    """
    Create and configure temporary directories for use by the test suite.

    Do not pollute the development/production directories, such as the store.
    Called through from LOGGING_CONFIG, after settings are fully processed.
    """
    tempdir = tempfile.mkdtemp(prefix="debusine-testsuite-data-")
    settings.DEBUSINE_DATA_PATH = tempdir
    settings.DEBUSINE_CACHE_DIRECTORY = os.path.join(tempdir, 'cache')
    settings.DEBUSINE_TEMPLATE_DIRECTORY = os.path.join(tempdir, 'templates')
    settings.DEBUSINE_UPLOAD_DIRECTORY = os.path.join(tempdir, 'uploads')
    settings.DEBUSINE_STORE_DIRECTORY = os.path.join(tempdir, 'store')
    settings.DEBUSINE_TOKENS_DIRECTORY = os.path.join(tempdir, 'tokens')
    os.mkdir(settings.DEBUSINE_CACHE_DIRECTORY)
    os.mkdir(settings.DEBUSINE_TEMPLATE_DIRECTORY)
    os.mkdir(settings.DEBUSINE_UPLOAD_DIRECTORY)
    os.mkdir(settings.DEBUSINE_STORE_DIRECTORY)
    os.mkdir(settings.DEBUSINE_TOKENS_DIRECTORY, mode=0o700)

    if hasattr(settings, "DEBUSINE_LOG_DIRECTORY"):
        settings.DEBUSINE_LOG_DIRECTORY = os.path.join(tempdir, 'logs')
        os.mkdir(settings.DEBUSINE_LOG_DIRECTORY)

    if settings.TEMPLATES:
        settings.TEMPLATES[0]["DIRS"] = [settings.DEBUSINE_TEMPLATE_DIRECTORY]

    # We could send signals for tests that cache settings, and restore them:
    # https://docs.djangoproject.com/en/3.2/ref/signals/#django.test.signals.setting_changed
    # but we're early in the start-up process, this complexifies the code,
    # and it's hard to assess the side effects, so let's not.


def cleanup_temp_directory() -> None:
    """Cleanup a temporary DEBUSINE_DATA_PATH that we created for tests."""
    data_path = Path(settings.DEBUSINE_DATA_PATH)

    temp_root = Path(tempfile.gettempdir())
    if data_path.is_relative_to(temp_root):
        shutil.rmtree(data_path)
    else:
        raise AssertionError(
            f"Not deleting test directory {str(data_path)!r} "
            f"(outside {str(temp_root)!r}).\n",
        )


def test_data_override(config: dict[str, Any]) -> None:
    """
    Setup temp data directories for testing.

    This is called through LOGGING_CONFIG, because that hooks is
    invoked after the settings are processed (including
    .__init__.compute_default_settings()), right before the
    application starts.
    """
    import logging.config

    from django.conf import settings

    test_mode = getattr(settings, "TEST_MODE", False)
    if test_mode:  # pragma: no branch
        # Replace and create DEBUSINE_*_DIRECTORY
        setup_temp_data_directories()

        if hasattr(settings, "DEBUSINE_LOG_DIRECTORY"):
            # Divert log handlers to new temp DEBUSINE_LOG_DIRECTORY
            for handler in config["handlers"].values():
                if "filename" in handler:
                    handler["filename"] = os.path.join(
                        settings.DEBUSINE_LOG_DIRECTORY,
                        os.path.basename(handler["filename"]),
                    )

        # Avoid writing test logs to journald
        if "journal" in config["handlers"]:
            config["handlers"]["journal"] = {
                "class": "logging.NullHandler",
            }

    # default logging config handler
    logging.config.dictConfig(config)
