# Generated by Django 3.2.12 on 2022-05-07 02:03
from __future__ import annotations

import re
from itertools import chain
from pathlib import Path
from typing import TYPE_CHECKING

from django.conf import settings
from django.db import migrations

if TYPE_CHECKING:
    from django.apps.registry import Apps
    from django.db.backends.base.schema import BaseDatabaseSchemaEditor

    from ..models import MediaFile

# Color constants for better readable output
ERROR: str = "\x1b[1;31m"
INFO: str = "\x1b[1;34m"
WARN: str = "\x1b[1;33m"
RESET: str = "\x1b[0;39m"


def ensure_exists(path: Path) -> Path:
    """
    Make sure the given path exists

    :param path: The path that should be created if not exists
    :return: The existing path
    """
    path.mkdir(parents=True, exist_ok=True)
    return path


def replace_media_prefix(media: MediaFile, before: str, after: str) -> None:
    """
    Helper function to replace the prefix of a media file path.

    :param media: The media file which should be moved
    :param before: The old location prefix
    :param after: The new location prefix
    """
    if str(media.file).startswith(before):
        media.file = after + str(media.file)[len(before) :]
        if media.thumbnail:
            media.thumbnail = after + str(media.thumbnail)[len(before) :]
        media.save()
    else:
        print(
            f"\n    ↳ {WARN}Warning:{RESET} Media element {media!r} is not located in the media/{before} directory, please fix the path manually ❌",
            end="",
        )


# pylint: disable=unused-argument
def rename_media_directories(
    apps: Apps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
    """
    Renames the location of the physical files of the already existing MediaFiles on the system.

    The global media library is now located at /media/global/...
    The region media library is now located at /media/regions/...

    :param apps: The configuration of installed applications
    :param schema_editor: The database abstraction layer that creates actual SQL code
    """
    # Rename all directories on the hard drive
    media_directory = Path(settings.MEDIA_ROOT)
    # Rename region directory
    region_directory = media_directory / "sites"
    if region_directory.is_dir():
        region_directory.rename(ensure_exists(media_directory / "regions"))
        print(
            f"\n    ↳ Renamed region media library directory {INFO}media/sites/{RESET} to {INFO}media/regions/{RESET} ✔",
            end="",
        )
    # Move global directories into "global/" subdirectory
    global_directory = ensure_exists(media_directory / "global")
    for directory in media_directory.iterdir():
        # Check if directory name is valid year
        if directory.name.isnumeric() and len(directory.name) == 4:
            directory.rename(ensure_exists(global_directory / directory.name))
            print(
                f"\n    ↳ Moved global media library directory from {INFO}media/{directory.name}/{RESET} to {INFO}media/global/{directory.name}/{RESET} ✔",
                end="",
            )
        else:
            print(
                f"\n    ↳ Skipped directory {INFO}media/{directory.name}/{RESET} because it does not appear to be a year",
                end="",
            )
    # Rename all files in the database
    MediaFile = apps.get_model("cms", "MediaFile")
    for media in MediaFile.objects.all():
        if media.region:
            replace_media_prefix(media, "sites/", "regions/")
        else:
            replace_media_prefix(media, "", "global/")
    print(
        "\n    ↳ Fixed all paths of media file objects in the database ✔",
        end="",
    )
    # Fix all media links in the database
    PageTranslation = apps.get_model("cms", "PageTranslation")
    EventTranslation = apps.get_model("cms", "EventTranslation")
    POITranslation = apps.get_model("cms", "POITranslation")
    for translation in chain(
        PageTranslation.objects.all(),
        EventTranslation.objects.all(),
        POITranslation.objects.all(),
    ):
        # Replace region directory
        translation.content = translation.content.replace(
            f"{settings.BASE_URL}/media/sites/", f"{settings.BASE_URL}/media/regions/"
        )
        # Replace global directory
        translation.content = re.sub(
            rf"{settings.BASE_URL}/media/(\d{{4}})/",
            rf"{settings.BASE_URL}/media/global/\1/",
            translation.content,
        )
        translation.save(update_fields=["content"])
    print(
        "\n    ↳ Fixed all translation content links to media file in the database ✔",
        end="",
    )


# pylint: disable=unused-argument
def reverse_rename_media_directories(
    apps: Apps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
    """
    Reverse the renaming of the media directories

    The global media library is reversed to /media/...
    The region media library is reversed to /media/sites/...

    :param apps: The configuration of installed applications
    :param schema_editor: The database abstraction layer that creates actual SQL code
    """
    # Rename all directories on the hard drive
    media_directory = Path(settings.MEDIA_ROOT)
    # Rename region directory
    region_directory = media_directory / "regions"
    if region_directory.is_dir():
        region_directory.rename(ensure_exists(media_directory / "sites"))
        print(
            f"\n    ↳ Renamed region media library directory from {INFO}media/regions/{RESET} to {INFO}media/sites/{RESET} ✔",
            end="",
        )
    # Move global directories directly into media directory
    global_directory = media_directory / "global"
    if global_directory.is_dir():
        for directory in global_directory.iterdir():
            directory.rename(ensure_exists(media_directory / directory.name))
            print(
                f"\n    ↳ Moved global media library directory from {INFO}media/global/{directory.name}/{RESET} to {INFO}media/{directory.name}/{RESET} ✔",
                end="",
            )
        global_directory.rmdir()
    # Rename all files in the database
    MediaFile = apps.get_model("cms", "MediaFile")
    for media in MediaFile.objects.all():
        if media.region:
            replace_media_prefix(media, "regions/", "sites/")
        else:
            replace_media_prefix(media, "global/", "")
    print(
        "\n    ↳ Fixed all paths of media file objects in the database ✔",
        end="",
    )
    # Fix all media links in the database
    PageTranslation = apps.get_model("cms", "PageTranslation")
    EventTranslation = apps.get_model("cms", "EventTranslation")
    POITranslation = apps.get_model("cms", "POITranslation")
    for translation in chain(
        PageTranslation.objects.all(),
        EventTranslation.objects.all(),
        POITranslation.objects.all(),
    ):
        # Replace region directory
        translation.content = translation.content.replace(
            f"{settings.BASE_URL}/media/regions/", f"{settings.BASE_URL}/media/sites/"
        )
        # Replace global directory
        translation.content = translation.content.replace(
            f"{settings.BASE_URL}/media/global/", f"{settings.BASE_URL}/media/"
        )
        translation.save(update_fields=["content"])
    print(
        "\n    ↳ Fixed all translation content links to media file in the database ✔",
        end="",
    )


class Migration(migrations.Migration):
    """
    Migration file to rename the media directories
    """

    dependencies = [
        ("cms", "0021_rename_location_not_on_map"),
    ]

    operations = [
        migrations.RunPython(
            rename_media_directories, reverse_rename_media_directories
        ),
    ]
