"""
Esup-Pod BBB command.

This command is used to manage the recordings made by BigBlueButton.
To achieve this, this command performs the following tasks:

- Connect to BBB / Scalelite server to get informations about the
current meetings and save then in Pod database.
This is useful to obtain the actuel meetings
and the moderators list of theses meetings.
Be careful: in BBB, we only have the firstname and last name
of these moderators.

- Search for recordings available for meetings.
Search for meetings, made since 4 days, with their presentation recorded
where the recording is not available for the moment.
The idea of the 4 days is to avoid to process recordings that were deleted
or with bad data in the database (in fact, the recording tag in BBB
seems always true even if not recorded).

- Search to matching BBB users as Pod users.
This allows to try if BBB user (known with firstname and lastname) is matching
a Pod user. You can parameter the BBB username format via the use
of BBB_USERNAME_FORMAT setting.
At each use of this script, we search to matching BBB users
- not already known - as Pod users.
Be careful: tested with the Moodle plugin, mod_bigbluebuttonbn,
not with Greenlight (should be the same if use of LDAP with givenName
and lastName).

- Then, we check directory (DEFAULT_BBB_PATH) to publish video files that were
generated by bbb-recorder (DEFAULT_BBB_PLUGIN). If video files found, this
script encode them as Pod video.

- If you have set BBB_NUMBER_DAYS_BEFORE_DELETE to a number different than 0,
all meetings - with associated users - not already published, older than
BBB_NUMBER_DAYS_BEFORE_DELETE days, will be deleted.

Finally, if there was at least one live started or one error,
an email is sent to Pod admins.

This script must be executed regurlaly (for an example, with a CRON task).
Example: crontab -e */2 * * * * /usr/bin/bash -c 'export
WORKON_HOME=/data/www/%userpod%/.virtualenvs; export
VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3.6; cd
/data/www/%userpod%/django_projects/podv3; source
/usr/bin/virtualenvwrapper.sh; workon django_pod; python manage.py bbb
main'
"""

import os
import traceback
from django.utils import translation
from django.core.management.base import BaseCommand
from django.conf import settings
from pod.bbb.models import BBB_Meeting as Meeting, Attendee
import hashlib
import requests
import datetime
import dateutil.parser
from django.core.mail import mail_admins
from django.utils import timezone
from defusedxml import minidom
import urllib.parse

from pod.video.models import Video, Type, get_storage_path_video
from pod.video_encode_transcript import encode
from django.contrib.auth.models import User

from django.db.models import Value
from django.db.models.functions import Concat

LANGUAGE_CODE = getattr(settings, "LANGUAGE_CODE", "fr")

# Use of BigBlueButton
USE_BBB = getattr(settings, "USE_BBB", False)

# Directory that will contain the video files generated by bbb-recorder
DEFAULT_BBB_PATH = getattr(settings, "DEFAULT_BBB_PATH", "/data/bbb-recorder/media/")
# The last caracter of DEFAULT_BBB_PATH must be an OS separator
if not DEFAULT_BBB_PATH.endswith(os.path.sep):
    DEFAULT_BBB_PATH += os.path.sep

# BigBlueButton or Scalelite server URL, where BBB Web presentation and API are
BBB_SERVER_URL = getattr(settings, "BBB_SERVER_URL", "https://bbb.univ.fr/")
# The last caracter of BBB_SERVER_URL must be /
if not BBB_SERVER_URL.endswith("/"):
    BBB_SERVER_URL += "/"

# BigBlueButton key or Scalelite LOADBALANCER_SECRET
BBB_SECRET_KEY = getattr(settings, "BBB_SECRET_KEY", "")

# Default type of the generated video
DEFAULT_BBB_TYPE_ID = getattr(settings, "DEFAULT_BBB_TYPE_ID", 1)

# Username format of the user in BBB
BBB_USERNAME_FORMAT = getattr(settings, "BBB_USERNAME_FORMAT", "first_name last_name")

# Number of days before removal the meetings
BBB_NUMBER_DAYS_BEFORE_DELETE = getattr(settings, "BBB_NUMBER_DAYS_BEFORE_DELETE", 0)

# BBB or Scalelite version is 2.3 (useful for recording URL in 2.0 or 2.3 format)
BBB_VERSION_IS_23 = getattr(settings, "BBB_VERSION_IS_23", False)

# Allowed extensions
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",
    ),
)

# Mode debug (0: False, 1: True)
DEBUG = getattr(settings, "DEBUG", False)
# Encode video
ENCODE_VIDEO = getattr(settings, "ENCODE_VIDEO", "start_encode")


def print_if_debug(str):
    if DEBUG:
        print(str)


def encode_file_exist(filename, extension, message_error, html_message_error):
    # A video file exist in the BBB directory: encode it
    print_if_debug(" - Encode BBB video file: " + filename)
    # Absolute path of the video
    source_file = os.path.join(DEFAULT_BBB_PATH, filename)

    # Filename corresponds to: internal_meeting_id.webm
    internalMeetingId = filename.replace("." + extension, "")
    # Check if the meeting already exists in Pod database
    oMeeting = Meeting.objects.filter(internal_meeting_id=internalMeetingId).first()
    if oMeeting:
        # Set video properties with meetng informations
        video = Video()
        video.title = oMeeting.meeting_name
        if oMeeting.encoded_by_id:
            video.owner = User.objects.get(id=oMeeting.encoded_by_id)
        video.type = Type.objects.get(id=DEFAULT_BBB_TYPE_ID)
        video.date_evt = oMeeting.session_date
        # Video management
        storage_path = get_storage_path_video(video, os.path.basename(source_file))
        dt = str(datetime.datetime.now()).replace(":", "-")
        nom, ext = os.path.splitext(os.path.basename(source_file))
        ext = ext.lower()
        # Video name
        video_name = nom + "_" + dt.replace(" ", "_") + ext
        video.video = os.path.join(os.path.dirname(storage_path), video_name)
        # Move source file to destination
        os.makedirs(os.path.dirname(video.video.path), exist_ok=True)
        os.rename(source_file, video.video.path)
        video.save()
        # Encode
        encode_video = getattr(encode, ENCODE_VIDEO)
        encode_video(video.id)
    else:
        # Meeting was certainly deleted in Pod database
        print_if_debug(
            " - WARNING: It seems that this meeting was deleted "
            "from Pod database. "
            "internal_meeting_id: " + internalMeetingId
        )

    return html_message_error, message_error


def process_directory(files, root, html_message_error, message_error):
    # Search files in the BBB directory
    for filename in files:
        # Name of the directory
        dirname = root.split(os.path.sep)[-1]
        print_if_debug(
            "\n*** Process the file "
            + os.path.join(DEFAULT_BBB_PATH, dirname, filename)
            + " ***"
        )
        # Check if extension is a good extension (videos extensions)
        extension = filename.split(".")[-1]

        valid_ext = VIDEO_ALLOWED_EXTENSIONS
        if not (extension in valid_ext and filename != extension):
            print_if_debug(
                " - WARNING: " + extension + "is not a valid video "
                "extension. If it should "
                "be, add it to the setting "
                "VIDEO_ALLOWED_EXTENSIONS"
            )
            continue
        html_message_error, message_error = encode_file_exist(
            filename, extension, message_error, html_message_error
        )

    return html_message_error, message_error


def get_bbb_meetings_by_xml(html_message_error, message_error):
    print_if_debug("\n*** Check BBB/Scalelite actual meetings  ***")
    try:
        # See https://docs.bigbluebutton.org/dev/api.html#usage
        # for checksum and security
        checksum = hashlib.sha1(
            str("getMeetings" + BBB_SECRET_KEY).encode("utf-8")
        ).hexdigest()
        # Request on BBB/Scalelite server (API)
        # URL example:
        # https://bbb.univ.fr/bigbluebutton/api/getMeetings?checksum=xxxx
        urlToRequest = BBB_SERVER_URL
        urlToRequest += "bigbluebutton/api/getMeetings?checksum=" + checksum
        addr = requests.get(urlToRequest)
        print_if_debug(
            "Request on URL: " + urlToRequest + ", status: " + str(addr.status_code)
        )
        # XML result to parse
        xmldoc = minidom.parseString(addr.text)
        returncode = xmldoc.getElementsByTagName("returncode")[0].firstChild.data
        # Management of FAILED error (basically error in checksum)
        if returncode == "FAILED":
            err = "Return code = FAILED for: " + urlToRequest
            err += " => : " + xmldoc.toxml() + ""
            message_error += err + "\n"
            html_message_error += "<li>" + err + "</li>"
        # Actual meetings
        meetings = xmldoc.getElementsByTagName("meeting")
        for meeting in meetings:
            get_meeting(meeting, html_message_error, message_error)

    except Exception as e:
        err = (
            "Problem to parse XML meetings on the BBB/Scalelite server "
            "or save in Pod database: " + str(e) + ". " + traceback.format_exc()
        )
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


def get_meeting(meeting, html_message_error, message_error):
    try:
        # Get meeting informations
        meetingName = meeting.getElementsByTagName("meetingName")[0].firstChild.data
        meetingID = meeting.getElementsByTagName("meetingID")[0].firstChild.data
        internalMeetingID = meeting.getElementsByTagName("internalMeetingID")[
            0
        ].firstChild.data
        date = meeting.getElementsByTagName("createDate")[0].firstChild.data
        # Recording seems useless (~always True)
        recording = meeting.getElementsByTagName("recording")[0].firstChild.data

        print_if_debug("\n - Meeting: " + internalMeetingID)

        # Id of the current meeting
        idActualMeeting = 0
        # Search if the meeting already exists in Pod database
        oMeeting = Meeting.objects.filter(internal_meeting_id=internalMeetingID).first()
        if oMeeting:
            idActualMeeting = oMeeting.id
            print_if_debug("   + Meeting already exists in Pod database.")
            # Check if meeting is recorded now
            if oMeeting.recorded is False and recording == "true":
                print_if_debug("   + Recording this meeting. ")
                oMeeting.recorded = True
            # In all case, save the last date where BBB session was in progress
            lastDateInProgress = timezone.now()
            oMeeting.last_date_in_progress = lastDateInProgress
            oMeeting.save()
        else:
            # Create the meeting in Pod database
            print_if_debug(
                "   + Create the meeting in Pod database. "
                "internal_meeting_id: " + internalMeetingID
            )
            meetingToCreate = Meeting()
            meetingToCreate.meeting_id = meetingID
            meetingToCreate.internal_meeting_id = internalMeetingID
            meetingToCreate.meeting_name = meetingName
            # Convert the date in the database format
            dateForSql = dateutil.parser.parse(date, ignoretz=False)
            meetingToCreate.session_date = dateForSql
            # Initially encoding_step = 0 (very important)
            meetingToCreate.encoding_step = 0
            # Recording tag seems ~always true, so seems useless
            if recording == "true":
                meetingToCreate.recorded = True
            else:
                meetingToCreate.recorded = False
            meetingToCreate.recording_available = False
            meetingToCreate.save()
            idActualMeeting = meetingToCreate.id

        # Management of the participants
        for attendee in meeting.getElementsByTagName("attendee"):
            get_attendee(attendee, idActualMeeting, html_message_error, message_error)

    except Exception as e:
        err = (
            "Problem to get BBB meeting "
            "and save in Pod database: " + str(e) + ". " + traceback.format_exc()
        )
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


def get_attendee(attendee, idActualMeeting, html_message_error, message_error):
    try:
        # In BigBlueButton, we have only the full name
        # Full name format: "first_name last_name"
        fullName = attendee.getElementsByTagName("fullName")[0].firstChild.data
        role = attendee.getElementsByTagName("role")[0].firstChild.data
        # We save only the BBB moderator
        if role == "MODERATOR":
            # Search if the BBB user already exists in Pod
            oAttendee = Attendee.objects.filter(
                full_name=fullName, meeting_id=idActualMeeting
            ).first()
            if oAttendee:
                print_if_debug(
                    "   + User already exists "
                    "in Pod database: "
                    "" + oAttendee.full_name
                )
            else:
                # Create the meeting user in Pod database
                print_if_debug(
                    "   + Create the meeting user in Pod database: " + fullName
                )
                attendeeToCreate = Attendee()
                attendeeToCreate.full_name = fullName
                attendeeToCreate.role = "MODERATOR"
                attendeeToCreate.meeting_id = idActualMeeting

                attendeeToCreate.save()
    except Exception as e:
        err = (
            "Problem to get BBB attendee "
            "and save in Pod database: " + str(e) + ". " + traceback.format_exc()
        )
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


def matching_bbb_pod_user(html_message_error, message_error):
    print_if_debug("\n*** Search if BBB users matching to Pod users ***")
    try:
        # Search for BBB users already in Pod database, without matching
        # By security: take only the 500 last BBB users, to avoid process
        # too long. Usefull when users are not known in Pod.
        attendees = Attendee.objects.filter(user_id__isnull=True).order_by("-id")[:500]

        # Use the BBB_USERNAME_FORMAT setting to make the matching.
        if BBB_USERNAME_FORMAT == "last_name first_name":
            bbbUsernameFormat = Concat("last_name", Value(" "), "first_name")
        else:
            bbbUsernameFormat = Concat("first_name", Value(" "), "last_name")

        for attendee in attendees:
            # Search if this BBB user matching to a Pod user.
            # Take the first one (This can cause an error in case of namesake!)
            podUser = (
                User.objects.annotate(
                    name=bbbUsernameFormat,
                )
                .filter(name__icontains=attendee.full_name)
                .first()
            )
            if podUser:
                # Update the id and the username of this user
                print_if_debug(
                    " - A Pod user matching a BBB user "
                    "was found in Pod database. "
                    "BBB user: " + attendee.full_name + ". "
                    "Pod user: " + podUser.username
                )
                attendee.username = podUser.username
                attendee.user_id = podUser.id
                attendee.save()
            else:
                print_if_debug(
                    " - A Pod user matching a BBB user "
                    "was NOT found in Pod database. "
                    "BBB user: " + attendee.full_name
                )

    except Exception as e:
        err = (
            "Problem to matching BBB user to Pod user: "
            + str(e)
            + ". "
            + traceback.format_exc()
        )
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


def get_bbb_meetings_recorded(html_message_error, message_error):
    print_if_debug("\n*** Check BBB meetings recorded in Pod, not already available ***")

    try:
        # Search for meetings, made since 4 days, with their presentation
        # recorded where the recording is not available for the moment.
        # The idea of the 4 days is to avoid to process recordings that
        # were deleted or with bad data in the database.
        # For informations: parameter Recorded seems useless (~always True)
        dateSince4d = timezone.now() - timezone.timedelta(days=4)
        meetings = Meeting.objects.filter(
            recorded=True,
            recording_available=False,
            session_date__gte=dateSince4d,
        ).order_by("id")
        for meeting in meetings:
            # Search recording on BBB/Scalelite server
            html_message_error, message_error = get_bbb_recording_by_xml(
                meeting.meeting_id,
                meeting.internal_meeting_id,
                html_message_error,
                message_error,
            )

    except Exception as e:
        err = (
            "Problem to get recorded meetings "
            "in Pod database: " + str(e) + ". " + traceback.format_exc()
        )
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


def get_bbb_recording_by_xml(
    meeting_id, internal_meeting_id, html_message_error, message_error
):
    print_if_debug(" - Check BBB/Scalelite recording.")
    try:
        # See https://docs.bigbluebutton.org/dev/api.html#usage
        # for checksum and security
        uri = "getRecordingsmeetingID="
        uri += urllib.parse.quote_plus(meeting_id) + BBB_SECRET_KEY
        checksum = hashlib.sha1(str(uri).encode("utf-8")).hexdigest()
        # Request on BBB/Scalelite server (API)
        # URL example: https://bbb.univ.fr/bigbluebutton/api/getRecordings?
        # meetingID=xxxxxxxxxxxxxx&checksum=yyyyyyyyyyyyyyy
        urlToRequest = BBB_SERVER_URL
        urlToRequest += "bigbluebutton/api/getRecordings?meetingID="
        urlToRequest += urllib.parse.quote_plus(meeting_id)
        urlToRequest += "&checksum=" + checksum
        addr = requests.get(urlToRequest)
        print_if_debug(
            "   + Request on URL: " + urlToRequest + ""
            ", status: " + str(addr.status_code)
        )
        # XML result to parse
        xmldoc = minidom.parseString(addr.text)
        returncode = xmldoc.getElementsByTagName("returncode")[0].firstChild.data
        # Management of FAILED error (basically error in checksum)
        if returncode == "FAILED":
            err = "Return code = FAILED for: " + urlToRequest
            err += " => : " + xmldoc.toxml() + ""
            message_error += err + "\n"
            html_message_error += "<li>" + err + "</li>"
        # Actual recordings
        recordings = xmldoc.getElementsByTagName("recording")
        for recording in recordings:
            get_recording(
                recording,
                internal_meeting_id,
                html_message_error,
                message_error,
            )

    except Exception as e:
        err = (
            "Problem to parse XML recording on the BBB/Scalelite server "
            "or save in Pod database: " + str(e) + ". " + traceback.format_exc()
        )
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


def get_recording(recording, internal_meeting_id, html_message_error, message_error):
    try:
        # Get recording informations
        # meetingID = recording.getElementsByTagName(
        #    "meetingID")[0].firstChild.data
        internalMeetingID = recording.getElementsByTagName("internalMeetingID")[
            0
        ].firstChild.data

        # We only process the correct recording,
        # set by the internal_meeting_id
        print_if_debug(internalMeetingID + " -- " + internal_meeting_id)
        if internalMeetingID == internal_meeting_id:
            # Check if the meeting already exists in Pod database
            oMeeting = Meeting.objects.filter(
                internal_meeting_id=internal_meeting_id
            ).first()
            if oMeeting:
                get_and_save_recording_url(
                    recording,
                    internal_meeting_id,
                    oMeeting,
                    html_message_error,
                    message_error,
                )
            else:
                # Meeting was certainly deleted in Pod database
                print_if_debug(
                    "   + WARNING: It seems that this "
                    "meeting was deleted from Pod database: " + internalMeetingID
                )

    except Exception as e:
        err = "Problem to get BBB recording: " + str(e) + ". " + traceback.format_exc()
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


def get_and_save_recording_url(
    recording, internal_meeting_id, oMeeting, html_message_error, message_error
):
    try:
        # Get recording URL that corresponds to the presentation URL
        # Save this information, if found, in database
        # Take only the "presentation" format
        # Not other format like "screenshare" or "podcast"
        recording_url = ""
        # Check playback data
        for playback in recording.getElementsByTagName("playback"):
            # Depends on BBB parameters, we can have multiple format
            for format in playback.getElementsByTagName("format"):
                type = format.getElementsByTagName("type")[0].firstChild.data
                # For bbb-recorder, we need URL of presentation format
                if type == "presentation":
                    # Recording URL is the BBB presentation URL
                    recording_url = format.getElementsByTagName("url")[0].firstChild.data
                    # We take the first thumbnail found
                    thumbnail_url = playback.getElementsByTagName("image")[
                        0
                    ].firstChild.data

        if recording_url != "":
            print_if_debug(
                "   + The recording was found. "
                "internal_meeting_id: " + internal_meeting_id + ". "
                "recording_url: " + recording_url
            )
            # Convert recording_url format (2.0 to 2.3) if necessary
            recording_url, html_message_error, message_error = convert_format(
                recording_url, internal_meeting_id, html_message_error, message_error
            )
            oMeeting.recording_available = True
            oMeeting.recording_url = recording_url
            oMeeting.thumbnail_url = thumbnail_url
            oMeeting.save()

    except Exception as e:
        err = (
            "Problem to get BBB recording url "
            "and save in Pod database: " + str(e) + ". " + traceback.format_exc()
        )
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


def convert_format(recording_url, internal_meeting_id, html_message_error, message_error):
    print_if_debug("\n*** Convert recording_url format (if necessary)  ***")
    try:
        # Conversion - if necessary - from
        # https://xxx/playback/presentation/2.0/playback.html?meetingId=ID
        # to https://xxx/playback/presentation/2.3/ID?meetingId=ID
        if BBB_VERSION_IS_23 and recording_url.find("/2.0/") >= 0:
            recording_url = recording_url.replace(
                "/2.0/playback.html", "/2.3/" + internal_meeting_id
            )
            print_if_debug(
                "   + Converting recording_url "
                "from 2.0 format to 2.3 format. "
                "New recording_url: " + recording_url
            )
    except Exception as e:
        err = "Problem to convert format: " + str(e) + ". " + traceback.format_exc()
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return recording_url, html_message_error, message_error


def delete_old_meetings(html_message_error, message_error):
    print_if_debug("\n*** Delete old meetings and BBB users ***")
    try:
        # Delete only old meetings if parameter set
        if BBB_NUMBER_DAYS_BEFORE_DELETE == 0:
            print_if_debug(" - BBB_NUMBER_DAYS_BEFORE_DELETE = 0: nothing to remove.")
        else:
            # Search for BBB meetings, not already published, older than
            # BBB_NUMBER_DAYS_BEFORE_DELETE days
            delay = timezone.timedelta(days=BBB_NUMBER_DAYS_BEFORE_DELETE)
            date_removal = timezone.now() - delay
            meetings = Meeting.objects.filter(
                encoding_step__lt=3, session_date__lte=date_removal
            ).order_by("id")
            for meeting in meetings:
                print_if_debug(
                    " - Removal of this meeting. "
                    "internal_meeting_id: " + meeting.internal_meeting_id + "."
                )
                meeting.delete()
    except Exception as e:
        err = "Problem to delete old meetings: " + str(e) + ". " + traceback.format_exc()
        message_error += err + "\n"
        html_message_error += "<li>" + err + "</li>"
        print_if_debug(err)
        return html_message_error, message_error

    return html_message_error, message_error


class Command(BaseCommand):
    # First possible argument: main
    args = "main"
    help = "Manage the BigBlueButton presentation "
    valid_args = ["main"]

    def add_arguments(self, parser):
        parser.add_argument("task")

    def handle(self, *args, **options):
        # Activate a fixed locale fr
        translation.activate(LANGUAGE_CODE)
        if options["task"] and options["task"] in self.valid_args:
            html_message_error = ""
            message_error = ""
            # Connect to BBB / Scalelite server to get infos
            # about the current meetings
            html_message_error, message_error = get_bbb_meetings_by_xml(
                html_message_error, message_error
            )

            # Search for recording available for meetings
            html_message_error, message_error = get_bbb_meetings_recorded(
                html_message_error, message_error
            )

            # Search to matching BBB users as Pod users
            html_message_error, message_error = matching_bbb_pod_user(
                html_message_error, message_error
            )

            # Check directory to publish video files
            for root, dirs, files in os.walk(DEFAULT_BBB_PATH):
                if "logs" in dirs:
                    dirs.remove("logs")
                html_message_error, message_error = process_directory(
                    files, root, html_message_error, message_error
                )

            # Delete old meetings and users
            html_message_error, message_error = delete_old_meetings(
                html_message_error, message_error
            )

            # If USE_BBB = True, if there was at least one error,
            # send an email to Pod admins
            if message_error != "":
                if USE_BBB:
                    print_if_debug(
                        "\n\n*** An email BBB job [Error(s) "
                        "encountered] was sent to Pod admins, with message: "
                        "***\n\n" + message_error
                    )
                    mail_admins(
                        "BBB job [Error(s) encountered]",
                        message_error,
                        fail_silently=False,
                        html_message=html_message_error,
                    )
                else:
                    print_if_debug(
                        "\n\n*** Error(s) encountered but no email sent "
                        "\n\n*** (USE_BBB = false) "
                        "***\n\nMessage: " + message_error
                    )
        else:
            print("*** Warning: you must give some arguments: %s ***" % self.valid_args)
