"""Esup-Pod import_video views.

More information on this module at: https://www.esup-portail.org/wiki/x/BQCnSw
"""

import logging
import os
import requests
import subprocess
import threading
import time

# For PeerTube download
import json

from .models import ExternalRecording
from .forms import ExternalRecordingForm
from .utils import StatelessRecording, download_video_file
from .utils import manage_recording_url, check_source_url, parse_remote_file
from .utils import save_video, secure_request_for_upload
from .utils import check_video_size, verify_video_exists_and_size
from .utils import define_dest_file_and_path, check_file_exists, move_file
from .utils import check_url_format_presentation, check_url_need_token
from .utils import get_playbacks_urls_with_token, TypeSourceURL
from datetime import datetime
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import PermissionDenied
from django.contrib import messages
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render, redirect
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.html import mark_safe
from django.utils.translation import gettext_lazy as _
from django.utils.text import get_valid_filename
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.csrf import ensure_csrf_cookie
from pod.import_video.utils import manage_download
from pod.main.views import in_maintenance
from pod.main.utils import secure_post_request, display_message_with_icon

# For Youtube download
from pytube import YouTube
from pytube.exceptions import PytubeError, VideoUnavailable

# To convert old BBB presentation
from pod.main.tasks import task_start_bbb_presentation_encode_and_upload_to_pod
from pod.main.tasks import task_start_bbb_presentation_encode_and_move_to_destination


RESTRICT_EDIT_IMPORT_VIDEO_ACCESS_TO_STAFF_ONLY = getattr(
    settings, "RESTRICT_EDIT_IMPORT_VIDEO_ACCESS_TO_STAFF_ONLY", True
)

DEFAULT_TYPE_ID = getattr(settings, "DEFAULT_TYPE_ID", 1)

VIDEO_ALLOWED_EXTENSIONS = getattr(
    settings,
    "VIDEO_ALLOWED_EXTENSIONS",
    (
        "3gp",
        "avi",
        "divx",
        "flv",
        "m2p",
        "m4v",
        "mkv",
        "mov",
        "mp4",
        "mpeg",
        "mpg",
        "mts",
        "wmv",
        "mp3",
        "ogg",
        "wav",
        "wma",
        "webm",
        "ts",
    ),
)

VIDEOS_DIR = getattr(settings, "VIDEOS_DIR", "videos")

# Use of Celery to encode
CELERY_TO_ENCODE = getattr(settings, "CELERY_TO_ENCODE", False)
# Use plugin bbb-recorder for import-video module
USE_IMPORT_VIDEO_BBB_RECORDER = getattr(settings, "USE_IMPORT_VIDEO_BBB_RECORDER", False)
# Directory of bbb-recorder plugin
IMPORT_VIDEO_BBB_RECORDER_PLUGIN = getattr(
    settings, "IMPORT_VIDEO_BBB_RECORDER_PLUGIN", "/data/bbb-recorder/"
)
# Directory that will contain the video files generated by bbb-recorder
IMPORT_VIDEO_BBB_RECORDER_PATH = getattr(
    settings, "IMPORT_VIDEO_BBB_RECORDER_PATH", "/data/bbb-recorder/media/"
)

log = logging.getLogger(__name__)


def secure_external_recording(request, recording: ExternalRecording) -> None:
    """Secure an external recording.

    Args:
        request (Request): HTTP request
        recording (ExternalRecording): ExternalRecording instance

    Raises:
        PermissionDenied: if user not allowed
    """
    if (
        recording
        and request.user != recording.owner
        and not (
            request.user.is_superuser
            or request.user.has_perm("import_video:view_recording")
        )
    ):
        display_message_with_icon(
            request, messages.ERROR, _("You cannot view this recording.")
        )
        raise PermissionDenied


def get_can_delete_external_recording(request, owner: User) -> bool:
    """Return True if current user can delete this recording."""
    can_delete = False

    # Only owner can delete this external recording
    if (
        request.user == owner
        or (request.user.is_superuser)
        or (request.user.has_perm("import_video:delete_external_recording"))
    ):
        can_delete = True
    return can_delete


@csrf_protect
@ensure_csrf_cookie
@login_required(redirect_field_name="referrer")
def upload_external_recording_to_pod(request, record_id: int):
    """Upload external recording to Pod.

    Args:
        request (Request): HTTP request
        recording_id (Integer): record id (in database)

    Raises:
        PermissionDenied: if user not allowed

    Returns:
        HTTP Response: Redirect to the external recordings list
    """
    recording = get_object_or_404(
        ExternalRecording, id=record_id, site=get_current_site(request)
    )

    # Secure this external recording
    secure_external_recording(request, recording)

    # Only POST request
    secure_post_request(request)

    msg = ""
    upload = False
    try:
        upload = upload_recording_to_pod(request, record_id)
    except ValueError as ve:
        args = ve.args[0]
        msg = ""
        if args.get("error"):
            msg += "<strong>%s</strong><br>" % (args["error"])
        if args.get("message"):
            msg += args["message"]
        if args.get("proposition"):
            msg += "<br><span class='proposition'>%s</span>" % (args["proposition"])
    if upload and msg == "":
        msg += _(
            "The recording has been uploaded to Pod. "
            "You can see the generated video in Dashboard."
        )
        display_message_with_icon(request, messages.INFO, msg)
    else:
        display_message_with_icon(request, messages.ERROR, msg)
    return redirect(reverse("import_video:external_recordings", args=()))


@login_required(redirect_field_name="referrer")
def external_recordings(request):
    """List external recordings.

    Args:
        request (Request): HTTP Request

    Returns:
        HTTPResponse: external recordings list
    """
    page_title = _("My external videos")

    if RESTRICT_EDIT_IMPORT_VIDEO_ACCESS_TO_STAFF_ONLY and request.user.is_staff is False:
        return render(
            request,
            "import_video/list.html",
            {"access_not_allowed": True, "page_title": page_title},
        )

    site = get_current_site(request)

    # List of the external recordings from the database
    external_recordings = ExternalRecording.objects.filter(
        owner__id=request.user.id, site=site
    )
    # | request.user.owners_external_recordings.all().filter(site=site)
    external_recordings = external_recordings.order_by("-start_at").distinct()

    recordings = []
    for data in external_recordings:
        recording = get_stateless_recording(request, data)
        recordings.append(recording)
    return render(
        request,
        "import_video/list.html",
        {
            "recordings": recordings,
            "page_title": page_title,
        },
    )


@csrf_protect
@ensure_csrf_cookie
@login_required(redirect_field_name="referrer")
def add_or_edit_external_recording(request, id=None):
    """Add or edit an external recording.

    Args:
        request (Request): HTTP request
        id (Integer, optional): external recording id. Defaults to None (for creation).

    Raises:
        PermissionDenied: if user not allowed

    Returns:
        HTTPResponse: edition page
    """
    if in_maintenance():
        return redirect(reverse("maintenance"))

    recording = (
        get_object_or_404(ExternalRecording, id=id, site=get_current_site(request))
        if id
        else None
    )

    # Secure external recording
    secure_external_recording(request, recording)

    if RESTRICT_EDIT_IMPORT_VIDEO_ACCESS_TO_STAFF_ONLY and request.user.is_staff is False:
        return render(
            request,
            "import_video/add_or_edit.html",
            {"access_not_allowed": True},
        )

    default_owner = recording.owner.pk if recording else request.user.pk
    form = ExternalRecordingForm(
        instance=recording,
        is_staff=request.user.is_staff,
        is_superuser=request.user.is_superuser,
        current_user=request.user,
        initial={"owner": default_owner, "id": id},
    )

    if request.method == "POST":
        form = ExternalRecordingForm(
            request.POST,
            instance=recording,
            is_staff=request.user.is_staff,
            is_superuser=request.user.is_superuser,
            current_user=request.user,
            current_lang=request.LANGUAGE_CODE,
        )
        if form.is_valid():
            recording = save_recording_form(request, form)
            display_message_with_icon(
                request, messages.SUCCESS, _("The changes have been saved.")
            )
            return redirect(reverse("import_video:external_recordings"))
        else:
            display_message_with_icon(
                request,
                messages.ERROR,
                _("One or more errors have been found in the form."),
            )

    page_title = (
        _("Edit the external video “%s”") % recording.name
        if recording
        else _("Create an external video")
    )
    return render(
        request,
        "import_video/add_or_edit.html",
        {"form": form, "page_title": mark_safe(page_title)},
    )


@csrf_protect
@ensure_csrf_cookie
@login_required(redirect_field_name="referrer")
def delete_external_recording(request, id: int):
    """Delete an external recording.

    Args:
        request (Request): HTTP request
        id (Integer): record id (in database)

    Raises:
        PermissionDenied: if user not allowed

    Returns:
        HTTP Response: Redirect to the recordings list
    """
    recording = get_object_or_404(
        ExternalRecording, id=id, site=get_current_site(request)
    )

    # Secure external recording
    secure_external_recording(request, recording)

    # Only POST request
    secure_post_request(request)

    msg = ""
    delete = False
    try:
        delete = recording.delete()
    except ValueError as ve:
        args = ve.args[0]
        msg = ""
        if args["error"]:
            msg += "<strong>%s</strong><br>" % (args["error"])
        if args["message"]:
            msg += args["message"]
    if delete and msg == "":
        msg += _("The external recording has been deleted.")
        display_message_with_icon(request, messages.SUCCESS, msg)
    else:
        display_message_with_icon(request, messages.ERROR, msg)
    return redirect(reverse("import_video:external_recordings", args=()))


def save_recording_form(request, form):
    """Save an external recording.

    Args:
        request (Request): HTTP request
        form (Form): recording form

    Returns:
        ExternalRecording: recording saved in database
    """
    recording = form.save(commit=False)
    recording.site = get_current_site(request)
    if (
        (request.user.is_superuser or request.user.has_perm("import_video:add_recording"))
        and request.POST.get("owner")
        and request.POST.get("owner") != ""
    ):
        recording.owner = form.cleaned_data["owner"]
    elif getattr(recording, "owner", None) is None:
        recording.owner = request.user

    recording.save()
    form.save_m2m()
    return recording


# ##############################    Upload recordings to Pod
def save_external_recording(user: User, record_id: int):
    """Save an external recording in database.

    Args:
        user (User): user who saved this recording
        record_id (Integer): id of the recording in database

    Raises:
        ValueError: if impossible creation
    """
    try:
        # Update the external recording
        recording, created = ExternalRecording.objects.update_or_create(
            id=record_id,
            defaults={"uploaded_to_pod_by": user},
        )
    except Exception as exc:
        msg = {}
        msg["error"] = _("Impossible to create the external recording")
        msg["message"] = str(exc)
        raise ValueError(msg)


def upload_recording_to_pod(request, record_id: int) -> bool:
    """Upload recording to Pod (main function).

    Args:
        request (Request): HTTP request
        record_id (Integer): id record in the database

    Raises:
        ValueError: exception raised if no URL found or other problem

    Returns:
        Boolean: True if upload achieved
    """
    try:
        # Management by type of recording
        recording = ExternalRecording.objects.get(id=record_id)

        # Check that request is correct for upload
        secure_request_for_upload(request)

        # Manage differents source types
        if recording.type == "youtube":
            return upload_youtube_recording_to_pod(request, record_id)
        elif recording.type == "peertube":
            return upload_peertube_recording_to_pod(request, record_id)
        else:
            # Upload a standard or BBB video file, or a BBB presentation
            return upload_video_recording_to_pod(request, record_id)
    except Exception as exc:
        msg = {}
        proposition = ""
        msg["error"] = _("Unable to upload the video to Pod")
        try:
            # Management of error messages from sub-functions
            message = exc.args[0]["message"]
            proposition = exc.args[0].get("proposition")
        except Exception:
            # Management of error messages in all cases
            message = str(exc)

        msg["message"] = mark_safe(message)
        msg["proposition"] = proposition
        raise ValueError(msg)


def upload_video_recording_to_pod(request, record_id: int):  # noqa: C901
    """Upload a standard or BBB video file, or a BBB presentation to Pod."""
    try:
        # Try to identify the type of the source URL (avoids multiple source types)
        type_source_url = check_source_url(request.POST.get("source_url"))
        if type_source_url is not None and type_source_url.type == "Mediacad":
            # Mediacad platform
            return upload_mediacad_recording_to_pod(request, record_id, type_source_url)
        elif type_source_url is not None and type_source_url.type == "BBB_Presentation":
            # Old BigBlueBlutton playback (presentation format):
            # convert this presentation in video and upload automatically to Pod
            # via an asynchronous task
            if USE_IMPORT_VIDEO_BBB_RECORDER:
                start_bbb_encode_presentation_and_upload_to_pod(
                    record_id, type_source_url.url, type_source_url.extension
                )
                return True
            else:
                return False
        elif type_source_url is not None and type_source_url.type == "BBB_ESR":
            # BBB ESR platform
            # For BBB ESR infrastructure, there is always a video playback
            return upload_bbb_esr_video_recording_to_pod(record_id, type_source_url.url)
        else:
            # Video file (or BBB video file, same process) source URL
            return upload_standard_video_recording_to_pod(record_id)
    except Exception as exc:
        msg = {}
        proposition = ""
        msg["error"] = _("Impossible to upload to Pod the video")
        try:
            # Management of error messages from sub-functions
            message = exc.args[0]["message"]
            proposition = exc.args[0].get("proposition")
        except Exception:
            # Management of error messages in all cases
            message = str(exc)

        msg["message"] = mark_safe(message)
        msg["proposition"] = proposition
        raise ValueError(msg)


def upload_standard_video_recording_to_pod(record_id: int) -> bool:
    """Upload a standard video file (or BBB video file) recording to Pod.

    Used with an URL.

    Args:
        record_id (Integer): id record in the database

    Raises:
        ValueError: exception raised if no video found at this URL

    Returns:
        Boolean: True if upload achieved
    """
    try:
        # Session useful to achieve requests (and keep cookies between)
        session = requests.Session()

        recording = ExternalRecording.objects.get(id=record_id)

        # Step 1: Download and parse the remote HTML file if necessary
        source_url = recording.source_url
        # Check if extension is a video extension
        extension = source_url.split(".")[-1].lower()

        # Name of the video file to add to the URL (if necessary)
        video_file_add = ""
        if extension not in VIDEO_ALLOWED_EXTENSIONS:
            # Download and parse the remote HTML file (BBB specific)
            video_file_add = parse_remote_file(session, source_url)
            # Extension overload
            extension = video_file_add.split(".")[-1].lower()

        # Verify that video exists and not oversized
        source_video_url = manage_recording_url(source_url, video_file_add)
        verify_video_exists_and_size(source_video_url)

        # Step 2: Define destination source file
        extension = source_video_url.split(".")[-1].lower()
        dest_file, dest_path = define_dest_file_and_path(
            recording.owner, recording.id, extension
        )

        # Step 3: Download the video file
        source_video_url = manage_download(session, source_url, video_file_add, dest_file)

        # Step 4: Save informations about the recording
        save_external_recording(recording.owner, record_id)

        # Step 5: Save and encode Pod video
        description = _(
            "This video was uploaded to Pod; its origin is %(type)s: "
            '<a href="%(url)s" target="_blank">%(url)s</a>'
        ) % {"type": recording.get_type_display(), "url": source_video_url}

        save_video(recording.owner, dest_path, recording.name, description)

        return True
    except Exception as exc:
        manage_standard_exception(exc)


def upload_bbb_esr_video_recording_to_pod(record_id: int, source_url: str) -> bool:
    """Upload a BBB - from BBB ESR infrastructure - video file recording to Pod.

    Useful for generating the single-use token required to access the video file.
    This is only possible for recordings made on the same BBB infrastructure used
    by the meetings module.
    """
    try:
        # Session useful to achieve requests (and keep cookies between)
        session = requests.Session()

        recording = ExternalRecording.objects.get(id=record_id)

        # Check if the URL is used by an infrastructure that need a token
        # In such a case, request the BBB infrastructure to get playbacks with token
        presentation_url, video_url = get_playbacks_urls_with_token(recording)
        if video_url != "":
            # Define the new source URL, with token
            recording.source_url = video_url
            source_url = video_url

        # Download and parse the remote HTML file (BBB specific)
        video_file_add = parse_remote_file(session, source_url)
        extension = video_file_add.split(".")[-1].lower()

        # Impossible to check simply that video exists
        # and not oversized for BBB ESR infrastructure.
        # This seems to be a technical constraint.

        # Step 2: Define destination source file
        dest_file, dest_path = define_dest_file_and_path(
            recording.owner, recording.id, extension
        )

        # Step 3: Download the video file
        manage_download(session, source_url, video_file_add, dest_file)

        # Step 4: Save informations about the recording
        save_external_recording(recording.owner, record_id)

        # Step 5: Save and encode Pod video
        description = _(
            "This video has been uploaded to Pod and its source is the virtual "
            "classroom solution of the French Ministry of Higher Education and Research."
        )

        save_video(recording.owner, dest_path, recording.name, description)

        return True
    except Exception as exc:
        manage_standard_exception(exc)


def upload_local_video_recording_to_pod(record_id: id, dest_file: str, dest_path: str):
    """Upload a local (typically in Pod filesystem) video file recording to Pod.

    Useful for video files that have been encoded following
    the recording of a BBB presentation.

    Args:
        record_id (Integer): id record in the database
        dest_file (String): destination filename
        dest_path (String): destination path

    Raises:
        ValueError: exception raised if no video found in the filesystem

    Returns:
        Boolean: True if upload achieved
    """
    try:
        recording = ExternalRecording.objects.get(id=record_id)

        # Step 1: Save informations about the recording
        save_external_recording(recording.owner, record_id)

        # Step 2: Save and encode Pod video
        description = _(
            "This video was uploaded to Pod; its origin is %(type)s: "
            '<a href="%(url)s" target="_blank">%(url)s</a>'
        ) % {"type": recording.get_type_display(), "url": recording.source_url}

        save_video(recording.owner, dest_path, recording.name, description)

        log.info("- Video file uploaded: %s" % dest_file)

        return True
    except Exception as exc:
        manage_standard_exception(exc)


def upload_mediacad_recording_to_pod(
    request, record_id: int, type_source_url: TypeSourceURL
):
    """Upload a Mediacad video file recording to Pod.

    Args:
        request (Request): HTTP request
        record_id (Integer): id record in the database
        type_source_url (TypeSourceURL): informations about source URL

    Raises:
        ValueError: exception raised if no video found at this URL

    Returns:
        Boolean: True if upload achieved
    """
    try:
        # Session useful to achieve requests (and keep cookies between)
        session = requests.Session()

        recording = ExternalRecording.objects.get(id=record_id)

        # Step 1: Define the source video file to download
        # See utils.py/check_source_url()
        source_video_url = type_source_url.url
        extension = type_source_url.extension

        # Verify that video exists and not oversised
        verify_video_exists_and_size(source_video_url)

        # Step 2: Define destination source file
        dest_file, dest_path = define_dest_file_and_path(
            recording.owner, recording.id, extension
        )

        # Step 3: Download the video file
        download_video_file(session, source_video_url, dest_file)

        # Step 4: Save informations about the recording
        recording_title = request.POST.get("recording_name")
        save_external_recording(recording.owner, record_id)

        # Step 5: Save and encode Pod video
        # Get description from JSON API
        description = get_mediacad_api_description(type_source_url)
        save_video(recording.owner, dest_path, recording_title, description)

        return True
    except Exception as exc:
        manage_standard_exception(exc)


def get_mediacad_api_description(type_source_url: TypeSourceURL) -> str:
    """Return description of a Mediacad video, after a call to Mediacad JSON API.

    Args:
        type_source_url (TypeSourceURL): informations about source URL
    Returns:
        String: video description
    """
    url_api_video = type_source_url.api_url
    mc_video_desc = ""
    mc_video_title = ""
    try:
        with requests.get(url_api_video, timeout=3, stream=True) as response:
            if response.status_code == 200:
                mc_video_json = json.loads(response.content.decode("utf-8"))
                mc_video_title = mc_video_json["title"]
                mc_video_desc = mc_video_json["description"]
                if mc_video_desc is not None:
                    mc_video_desc = mc_video_desc.replace("\r\n", "<br>")
    except Exception:
        # Request API is a bonus (for title and description)
        pass
    description = _(
        "This video “%(name)s” was uploaded to Pod; its origin is %(type)s: "
        '<a href="%(url)s" target="_blank">%(url)s</a><br><br>%(desc)s'
    ) % {
        "name": mc_video_title,
        "type": "Mediacad",
        "url": type_source_url.url,
        "desc": mc_video_desc,
    }
    return description


def upload_youtube_recording_to_pod(request, record_id: int):
    """Upload Youtube recording to Pod.

    Use PyTube with its API
    More information: https://pytube.io/en/latest/api.html
    Args:
        request (Request): HTTP request
        record_id (Integer): id record in the database

    Raises:
        ValueError: exception raised if no YouTube video found or content inaccessible

    Returns:
        Boolean: True if upload achieved
    """
    try:
        # Manage source URL from video playback
        source_url = request.POST.get("source_url")

        # Use pytube to download Youtube file
        yt_video = YouTube(
            source_url,
            # on_complete_callback=complete_func,
            # use_oauth=True,
            # allow_oauth_cache=True
        )
        # Publish date (format: 2023-05-13 00:00:00)
        # Event date (format: 2023-05-13)
        date_evt = str(yt_video.publish_date)[0:10]

        # Setting video resolution
        yt_stream = yt_video.streams.get_highest_resolution()

        # Verify that video not oversized
        check_video_size(yt_stream.filesize)

        # User directory
        dest_dir = os.path.join(
            settings.MEDIA_ROOT,
            VIDEOS_DIR,
            request.user.owner.hashkey,
        )
        os.makedirs(os.path.dirname(dest_dir), exist_ok=True)

        discrim = datetime.now().strftime("%Y%m%d%H%M%S")
        filename = "%s-%s" % (discrim, get_valid_filename(yt_stream.default_filename))
        # Video file path
        dest_path = os.path.join(
            VIDEOS_DIR,
            request.user.owner.hashkey,
            filename,
        )

        # Download video
        yt_stream.download(dest_dir, filename=filename)

        # Step 4: Save informations about the recording
        save_external_recording(request.user, record_id)

        # Step 5: Save and encode Pod video
        description = _(
            "This video “%(name)s” was uploaded to Pod; "
            'its origin is Youtube: <a href="%(url)s" target="_blank">%(url)s</a>'
        ) % {"name": yt_video.title, "url": source_url}
        recording_title = request.POST.get("recording_name")
        save_video(request.user, dest_path, recording_title, description, date_evt)
        return True

    except VideoUnavailable:
        msg = {}
        msg["error"] = _("YouTube error")
        msg["message"] = _(
            "YouTube content is unavailable. "
            "This content does not appear to be publicly available."
        )
        msg["proposition"] = _(
            "Try changing the access rights to the video directly in Youtube."
        )
        raise ValueError(msg)
    except PytubeError as pterror:
        msg = {}
        msg["error"] = _("YouTube error “%s”" % (mark_safe(pterror)))
        msg["message"] = "%s\n%s" % (
            _("YouTube content is inaccessible."),
            _("This content does not appear to be publicly available."),
        )
        msg["proposition"] = _("Try changing the address of this recording.")
        raise ValueError(msg)
    except Exception as exc:
        manage_standard_exception(exc)


def upload_peertube_recording_to_pod(request, record_id: int) -> bool:  # noqa: C901
    """Upload Peertube recording to Pod.

    More information: https://docs.joinpeertube.org/api/rest-getting-started
    Args:
        request (Request): HTTP request
        record_id (Integer): id record in the database

    Raises:
        ValueError: exception raised if no PeerTube video found in this URL

    Returns:
        Boolean: True if upload achieved
    """
    try:
        # Session useful to achieve requests (and keep cookies between)
        session = requests.Session()

        # Manage source URL from video playback
        source_url = request.POST.get("source_url")

        # Check if extension is a video extension
        extension = source_url.split(".")[-1].lower()
        if extension in VIDEO_ALLOWED_EXTENSIONS:
            # URL corresponds to a video file. Format example:
            #  - https://xxxx.fr/download/videos/id-quality.mp4
            # with: id = id/uuid/shortUUID, quality=480/720/1080
            source_video_url = source_url
            # PeerTube API for this video:
            # https://xxxx.fr/api/v1/videos/id
            pos_pt = source_url.rfind("-")
            if pos_pt != -1:
                url_api_video = source_url[0:pos_pt].replace(
                    "/download/videos/", "/api/v1/videos/"
                )
            else:
                msg = {}
                msg["error"] = _("PeerTube error")
                msg["message"] = _(
                    "The address entered does not appear to be a valid PeerTube address."
                )
                msg["proposition"] = _("Try changing the address of the recording.")
                raise ValueError(msg)
        else:
            # URL corresponds to a PeerTube URL. Format example:
            #  - https://xxx.fr/w/id
            #  - https://xxx.fr/videos/watch/id
            # with: id = id/uuid/shortUUID
            # PeerTube API for this video:
            # https://xxxx.fr/api/v1/videos/id
            url_api_video = source_url.replace("/w/", "/api/v1/videos/")
            url_api_video = url_api_video.replace("/videos/watch/", "/api/v1/videos/")

        with requests.get(url_api_video, timeout=(10, 180), stream=True) as response:
            if response.status_code != 200:
                msg = {}
                msg["error"] = _("PeerTube error")
                msg["message"] = _(
                    "The address entered does not appear to be a valid PeerTube address "
                    "or the PeerTube server is not responding as expected."
                )
                msg["proposition"] = _(
                    "Try changing the address of the recording or retry later."
                )
                raise ValueError(msg)
            else:
                pt_video_json = json.loads(response.content.decode("utf-8"))
                # URL
                pt_video_url = pt_video_json["url"]
                # UUID, useful for the filename
                pt_video_uuid = pt_video_json["uuid"]
                pt_video_name = pt_video_json["name"]
                pt_video_description = pt_video_json["description"]
                if pt_video_description is None:
                    pt_video_description = ""
                else:
                    pt_video_description = pt_video_description.replace("\r\n", "<br>")
                # Creation date (format: 2023-05-23T08:16:34.690Z)
                pt_video_created_at = pt_video_json["createdAt"]
                # Evant date (format: 2023-05-23)
                date_evt = pt_video_created_at[0:10]
                # Source video file
                source_video_url = pt_video_json["files"][0]["fileDownloadUrl"]

        # Verify that video exists and not oversized
        verify_video_exists_and_size(source_video_url)

        # Step 2: Define destination source file
        extension = source_video_url.split(".")[-1].lower()
        dest_file, dest_path = define_dest_file_and_path(
            request.user, pt_video_uuid, extension
        )

        # Step 3: Download the video file
        download_video_file(session, source_video_url, dest_file)

        # Step 4: Save informations about the recording
        recording_title = request.POST.get("recording_name")
        save_external_recording(request.user, record_id)

        # Step 5: Save and encode Pod video
        description = _(
            "This video “%(name)s” was uploaded to Pod; its origin is PeerTube: "
            "<a href='%(url)s' target='blank'>%(url)s</a>."
        ) % {"name": pt_video_name, "url": pt_video_url}
        description = ("%s<br>%s") % (description, pt_video_description)
        save_video(request.user, dest_path, recording_title, description, date_evt)

        return True
    except Exception as exc:
        manage_standard_exception(exc)


def start_bbb_encode_presentation_and_upload_to_pod(
    record_id: int, url: str, extension: str
):
    """Send an asynchronous task or a thread to encode a BBB presentation.

    into a video file and upload it to Pod.

    With Celery, logs can be found in encoding servers, worker.log.
    Without Celery, a thread is run to do the job.
    """
    if CELERY_TO_ENCODE:
        task_start_bbb_presentation_encode_and_upload_to_pod.delay(
            record_id, url, extension
        )
    else:
        log.info(
            "START BBB ENCODE PRESENTATION/UPLOAD FOR EXTERNAL RECORDING %s" % record_id
        )
        # Thread use
        t = threading.Thread(
            target=bbb_encode_presentation_and_upload_to_pod,
            args=[record_id, url, extension],
        )
        t.setDaemon(True)
        t.start()


def start_bbb_encode_presentation_and_move_to_destination(
    filename: str, url: str, dest_file: str
):
    """Send an asynchronous task to encode or encode direclty a BBB presentation.

    into a video file and move it to a specific directory.

    With Celery, logs can be found in encoding servers, worker.log.
    Without Celery, we didn't use a thread because it caused problems
    for the script importing old BBB records.
    """
    if CELERY_TO_ENCODE:
        task_start_bbb_presentation_encode_and_move_to_destination.delay(
            filename, url, dest_file
        )
    else:
        log.info("START BBB ENCODE PRESENTATION/MOVE FOR %s" % filename)
        # No thread here
        bbb_encode_presentation_and_move_to_destination(filename, url, dest_file)


def bbb_encode_presentation(filename: str, url: str) -> str:
    """Encode a BBB presentation into a video file.

    Use bbb-recorder github project to do this.
    The logs for this operation can be found in
    IMPORT_VIDEO_BBB_RECORDER_PATH/logs/record_id.log.
    The video file is generated in IMPORT_VIDEO_BBB_RECORDER_PATH/.
    """
    # Logs
    logname = "%s.log" % filename
    logpath = os.path.join(IMPORT_VIDEO_BBB_RECORDER_PATH, "logs", logname)

    # The command looks like:
    # node export.js https://bbb.univ.fr/playback/presentation/2.0/
    # playback.html?meetingId=INTERNAL_MEETING_ID INTERNAL_MEETING_ID.webm
    # > /data/www/USERPOD/bbb-recorder/logs/INTERNAL_MEETING_ID.log
    # 2>&1 < /dev/null
    # Recording_URL can be also like https://bbb.univ.fr/playback/presentation/2.3/
    # INTERNAL_MEETING_ID/?meetingId=INTERNAL_MEETING_ID/
    # Encode the presentation in webm (not mp4, less data in log)
    # Put the generated video file on the bbb-recorder plugin directory
    command = "cd %s; node export.js %s %s > %s 2>&1 < /dev/null" % (
        IMPORT_VIDEO_BBB_RECORDER_PLUGIN,
        url,
        filename,
        logpath,
    )
    log.info("- bbb_encode_presentation")
    log.info("- Command: %s" % command)
    log.info("- Start encoding: %s" % time.ctime())

    # Execute the process
    result = subprocess.run(
        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
    )
    log.info("- Process result: %s" % result)
    log.info("- End encoding: %s" % time.ctime())

    source_generated_file = os.path.join(
        IMPORT_VIDEO_BBB_RECORDER_PATH,
        filename,
    )
    log.info("- Generated file: %s" % source_generated_file)
    # returns the path and filename of the generated video file
    return source_generated_file


def bbb_encode_presentation_and_move_to_destination(
    filename: str, url: str, dest_file: str
):
    """Encode a BBB presentation into a video file and put it on a specific directory.

    Use bbb-recorder github project to do this.
    The logs for this operation can be found in IMPORT_VIDEO_BBB_RECORDER_PATH/logs/.log.
    The video file is generated in IMPORT_VIDEO_BBB_RECORDER_PATH/ then moved
    to the destination (dest_file).
    """
    # Achieve the encode of the presentation via bbb-recorder
    source_generated_file = bbb_encode_presentation(filename, url)

    if check_file_exists(source_generated_file):
        log.info("- Video file generated: %s" % source_generated_file)

        # Move generated video file into the good directory
        move_file(source_generated_file, dest_file)

        log.info("- Video file moved to: %s" % dest_file)
    else:
        log.error("- Video file not generated: %s" % source_generated_file)


def bbb_encode_presentation_and_upload_to_pod(record_id: int, url: str, extension: str):
    """Encode a BBB presentation into a video file and upload it to Pod.

    Use bbb-recorder github project to do this.
    The logs for this operation can be found in
    IMPORT_VIDEO_BBB_RECORDER_PATH/logs/record_id.log.
    The video file is generated in IMPORT_VIDEO_BBB_RECORDER_PATH/ then moved
    to the user directory and finally saved and encoded in Pod.
    """
    # Get the record
    recording = ExternalRecording.objects.get(id=record_id)
    filename = str(record_id) + "." + extension

    dest_file, dest_path = define_dest_file_and_path(
        recording.owner, recording.id, extension
    )
    # Change the recording state
    recording.state = _("Convert web presentation to video in progress…")
    recording.save()

    # Achieve the encode of the presentation via bbb-recorder
    source_generated_file = bbb_encode_presentation(filename, url)

    if check_file_exists(source_generated_file):
        log.info("- Video file generated: %s" % source_generated_file)

        # Move generated video file into the good directory
        move_file(source_generated_file, dest_file)

        log.info("- Video file moved to: %s" % dest_file)

        # Finally upload the generated video file to Pod
        if upload_local_video_recording_to_pod(record_id, dest_file, dest_path):
            recording.state = _("Video file already uploaded to Pod")
            recording.save()

    else:
        # Video file not generated: inform the user via the recording state
        recording.state = _(
            "Impossible to upload to Pod the video, "
            "the link provided does not seem valid."
        )
        recording.save()


def manage_standard_exception(exc: Exception):
    """Manage standard exception to raise a specific error."""
    msg = {}
    msg["error"] = _("Impossible to upload to Pod the video")
    try:
        # Management of error messages from sub-functions
        message = "%s %s" % (exc.args[0]["error"], exc.args[0]["message"])
    except Exception:
        # Management of error messages in all cases
        message = str(exc)

    msg["message"] = mark_safe(message)
    msg["proposition"] = _("Try changing the record type or address for this recording.")
    raise ValueError(msg)


def get_stateless_recording(request, data: ExternalRecording):
    """Return a stateless recording from an external recording.

    Args:
        request (Request): HTTP request
        data (ExternalRecording): external recording

    Returns:
        StatelessRecording: stateless recording
    """
    recording = StatelessRecording(data.id, data.name, None)
    # By default, upload to Pod is possible
    recording.canUpload = True
    # Only owner can delete this external recording
    recording.canDelete = get_can_delete_external_recording(request, data.owner)

    recording.startTime = data.start_at

    recording.uploadedToPodBy = data.uploaded_to_pod_by

    # Status management
    recording.state = get_status_recording(data)

    # Management of the external recording type
    if data.type == "bigbluebutton" or data.type == "video":
        # Manage BBB recording URL
        if check_url_format_presentation(data.source_url):
            # Presentation format
            recording.presentationUrl = data.source_url
        else:
            # Management of other situations
            recording.videoUrl = data.source_url

        # Check if the URL is used by an infrastructure that need a token
        recording.recordingId = check_url_need_token(data.source_url)

        # If possible to encode BBB presentation
        # Useful for old BBB or BBB 2.6+ without video playback
        if USE_IMPORT_VIDEO_BBB_RECORDER and recording.presentationUrl != "":
            recording.canUpload = True
            recording.videoUrl = recording.presentationUrl

        # To optimize, do not check that url exists
        if recording.videoUrl == "":
            recording.state = _(
                "No video file found. Upload to Pod as a video is not possible."
            )
            recording.canUpload = False
            recording.videoUrl = ""
    else:
        # For PeerTube, Youtube
        recording.videoUrl = data.source_url

    # Display type label
    recording.type = data.get_type_display

    return recording


def get_status_recording(data: ExternalRecording) -> str:
    """Get the status of an external recording."""
    if data.uploaded_to_pod_by is None and data.state is None:
        state = _("Video file not uploaded to Pod")
    elif data.uploaded_to_pod_by is not None:
        state = _("Video file already uploaded to Pod")
    else:
        state = data.state
    return state


@csrf_protect
@ensure_csrf_cookie
@login_required(redirect_field_name="referrer")
def recording_with_token(request, id):
    """Get for specific recording.

    (recording created on BBB infrastructure that need a
    token and used by meeting module), the presentation and video source URL, in JSON.

    Args:
        request (Request): HTTP request
        record_id (int): record id

    Raises:
        PermissionDenied: if user not allowed

    Returns:
        HTTPResponse: internal recording (JSON format)
    """
    recording = ExternalRecording.objects.get(id=id)

    # Check if the URL is used by an infrastructure that need a token
    # In such a case, request the BBB infrastructure to get playbacks with token
    presentation_url, video_url = get_playbacks_urls_with_token(recording)

    # JSON format
    data = '{"presentationUrl": "%s", "videoUrl": "%s"}' % (presentation_url, video_url)
    if request.is_ajax():
        return HttpResponse(data, content_type="application/json")
    else:
        return HttpResponseBadRequest()
