"""
Set of helper functions that contribute to the correct execution and enhanced structure/modularity of major module functions (e.g. views).
"""

from django import forms
from django.template.response import TemplateResponse
from .auth import PLATFORM_SESSION_KEY
from .models import Platform

# Create your helper functions here.

# For more information on view-related subjects, e.g. using request/response objects, making database queries, working with forms, etc., see views.py .

def get_all_platform_names_from_DB():
    """
    Return the list (QuerySet) of all the platform names that exist in usermergeDB. This list can, among others, be used in the context
    of application pages/templates to display the name options of the platform field in forms.
    """
    return Platform.objects.values_list('name', flat = True)

def get_form_error_messages(form):
    """
    Check if the given form is an instance of "django.forms.forms.Form" class. If it is, return the list of all the ValidationError
    exception messages that are associated with its fields - if the form is not bound to any set of data, i.e. form.is_bound returns
    False, or the (bound) form field validation succeeds, i.e. form.is_valid() returns True, the list will be empty and vice versa.
    Otherwise, raise a TypeError exception with the appropriate error message.
    """
    if isinstance(form, forms.Form):
        form_error_values = list(form.errors.values())
        return [form_error_values[x][y] for x in range(len(form_error_values)) for y in range(len(form_error_values[x]))]
    else:
        raise TypeError('The form argument of get_form_error_messages() helper function '
                        'should be an instance of "django.forms.forms.Form" class!')

def seq_to_aligned_str(seq, max_line_len):
    """
    Create an aligned string from the elements of the given sequence. This string separates every max_line_len elements (along with the
    delimiting commas) by a newline character without changing their order (the last line may contain less than max_line_len elements).
    If max_line_len is a non-positive number, raise a ValueError exception with the appropriate error message.
    """
    if max_line_len <= 0:
        raise ValueError('The max_line_len argument of seq_to_aligned_str() helper function should be a positive integer!')
    seq = list(seq) # Turn seq into a list to ensure that it has the appropriate sequence format.
    aligned_str = ''
    for idx in range(0, len(seq), max_line_len):
        if idx + max_line_len < len(seq):
            aligned_str += str(seq[idx:idx + max_line_len])[1:-1] + ',\n'
        else:
            aligned_str += str(seq[idx:len(seq)])[1:-1]
    return aligned_str

def _update_user_profile(request, changed_data, cleaned_data):
    """
    [Acts as inner function of the edit_user_profile() view] Check which fields of the user profile edit form have been changed, update
    the session user's profile in usermergeDB with the corresponding validated (cleaned) and adequate values and display the updated
    profile along with the appropriate success message in the user profile edit page.
    """
    session_user = request.user
    if 'first_name' in changed_data:
        session_user.first_name = cleaned_data['first_name']
    if 'last_name' in changed_data:
        session_user.last_name = cleaned_data['last_name']
    if 'ece_id' in changed_data:
        session_user.ece_id = cleaned_data['ece_id']
    if 'email' in changed_data:
        session_user.email = cleaned_data['email']
    session_user.save(update_fields = changed_data)
    request.user = session_user # Update the profile in the user profile edit page via the associated request.
    return TemplateResponse(request, 'user_profile_edit.html',
                            {'success_message': 'Το προφίλ σας ενημερώθηκε επιτυχώς με χρήση των τροποποιηθέντων στοιχείων!'})

def _display_user_profile_edit_error_messages(request, error_data):
    """
    [Acts as inner function of the edit_user_profile() view] Create the appropriate post-validation error messages by using the
    validated (cleaned), yet inadequate, ece_id or/and email values of the user profile edit form along with the corresponding
    error codes (in the registered_ece_id_was_cleared code case, the validated ece_id is None and the non-None session user's
    ece_id is used instead), display them in the user profile edit page and include the error codes in the page context.
    """
    error_messages = []
    error_codes = []
    # The duplicate_ece_id and registered_ece_id_was_cleared errors can never occur both at the same time.
    for code in error_data:
        if code == 'duplicate_ece_id':
            error_messages.append('O δηλωθείς αριθμός μητρώου <' + error_data[code] + '> '
                                  'έχει ήδη καταχωρηθεί από άλλο χρήστη της βάσης!\n'
                                  'Παρακαλούμε ελέγξτε την ορθότητα του αριθμού μητρώου και δοκιμάστε ξανά!')
            error_codes.append(code)
        elif code == 'duplicate_email':
            error_messages.append('Το δηλωθέν ηλεκτρονικό ταχυδρομείο <' + error_data[code] + '> '
                                  'έχει ήδη καταχωρηθεί από άλλο χρήστη της βάσης!\n'
                                  'Παρακαλούμε ελέγξτε την ορθότητα του ηλεκτρονικού ταχυδρομείου και δοκιμάστε ξανά!')
            error_codes.append(code)
        else: # code == 'registered_ece_id_was_cleared'
            error_messages.append('Στο προφίλ σας είναι ήδη καταχωρημένος o αριθμός μητρώου <' + error_data[code] + '>, '
                                  'ο οποίος επιτρέπεται\n'
                                  'μεν να τροποποιηθεί έγκυρα αλλά απαγορεύεται να διαγραφεί οριστικά!')
            error_codes.append(code)
    return TemplateResponse(request, 'user_profile_edit.html', {'error_messages': error_messages,
                                                                'post_validation_error_codes': error_codes})

def _display_user_profile_recovery_success_message(request, recov_user):
    """
    [Acts as inner function of the search_for_recovery_user_profile() view] Display the appropriate success message in the user profile
    recovery page by taking the recovery user profile (non-empty User instance that corresponds to both the validated - cleaned - and
    adequate ece_id and email values of the corresponding form) into account. This message presents the (empty) session user with all
    the field values of the aforementioned profile and informs him/her that if he/she proceeds with the profile recovery, he/she will
    get logged out after his/her credentials for the login platform have first been associated with this profile (they will replace any
    previously associated ones). Finally, enable the recovery procedure - see the recover_user_profile() view - by storing the id of
    the recovery user profile in the session data as the recov_user_id value (it replaces any previously stored one).
    """
    request.session['recov_user_id'] = recov_user.id
    success_message = ('Τα δηλωθέντα στοιχεία εντοπίστηκαν επιτυχώς στη βάση και παραπέμπουν στο εξής καταχωρημένο προφίλ:\n\n'
                       'Όνομα: %s\n'
		       'Επώνυμο: %s\n'
		       'Αριθμός μητρώου: %s\n'
		       'Ηλεκτρονικό ταχυδρομείο: %s\n\n'
		       'Αν επιθυμείτε τη συσχέτιση του προαναφερθέντος προφίλ με τα διαπιστευτήρια εισόδου σας ως χρήστης\n'
                       'του %s (σε περίπτωση αλλαγμένων διαπιστευτηρίων, τα προϋπάρχοντα θα διαγραφούν από τη βάση),\n'
                       'παρακαλούμε ανακτήστε τα παραπάνω στοιχεία και εξέλθετε από το σύστημα!'
                      ) % (recov_user.first_name, recov_user.last_name,
                           '[Δεν έχει καταχωρηθεί]' if recov_user.ece_id is None else recov_user.ece_id,
                           recov_user.email, request.session[PLATFORM_SESSION_KEY])
    return TemplateResponse(request, 'user_profile_recovery.html', {'success_message': success_message})

def _display_user_profile_recovery_error_messages(request, error_data):
    """
    [Acts as inner function of the search_for_recovery_user_profile() view] Create the appropriate post-validation error messages
    by using the validated (cleaned), yet inadequate, ece_id or/and email values of the user profile recovery form along with the
    corresponding error codes, display them in the user profile recovery page and include the error codes in the page context. Since
    no recovery user profile (non-empty User instance that corresponds to both of the aforementioned form values) has been specified
    in usermergeDB, disable the recovery procedure - see the recover_user_profile() view - by ensuring that the recov_user_id value
    does not exist in the session data (if it exists, delete it).
    """
    if 'recov_user_id' in request.session:
        del request.session['recov_user_id']
    error_messages = []
    error_codes = []
    # The ece_id_and_email_exist_in_different_profiles error can only occur by itself. This means that if it occurs,
    # neither of the non_existent_ece_id and non_existent_email errors can occur at the same time.
    for code in error_data:
        if code == 'non_existent_ece_id':
            if error_data[code] is None:
                error_messages.append('Στη βάση δεν εντοπίστηκε κανένα προφίλ χρήστη που να συνδυάζει "κενό" αριθμό μητρώου\n'
                                      'με καταχωρημένο ηλεκτρονικό ταχυδρομείο! Παρακαλούμε δοκιμάστε ξανά με έναν\n'
                                      'έγκυρο οκταψήφιο αριθμό μητρώου!')
            else:
                error_messages.append('Ο δηλωθείς αριθμός μητρώου <' + error_data[code] + '> δεν είναι καταχωρημένος στη βάση!\n'
                                      'Παρακαλούμε ελέγξτε την ορθότητα του αριθμού μητρώου και δοκιμάστε ξανά!')
            error_codes.append(code)
        elif code == 'non_existent_email':
            error_messages.append('Το δηλωθέν ηλεκτρονικό ταχυδρομείο <' + error_data[code] + '> δεν είναι καταχωρημένο στη βάση!\n'
                                  'Παρακαλούμε ελέγξτε την ορθότητα του ηλεκτρονικού ταχυδρομείου και δοκιμάστε ξανά!')
            error_codes.append(code)
        else: # code == 'ece_id_and_email_exist_in_different_profiles'
            if error_data[code]['ece_id'] is None:
                error_messages.append('Το δηλωθέν ηλεκτρονικό ταχυδρομείο <' + error_data[code]['email'] + '> '
                                      'βρέθηκε καταχωρημένο στη βάση,\n'
                                      'ενώ σε αυτήν εντοπίστηκε και (τουλάχιστον ένα) προφίλ χρήστη που συνδυάζει '
                                      '"κενό" αριθμό μητρώου με\n'
                                      'καταχωρημένο ηλεκτρονικό ταχυδρομείο! Η αναζήτηση, όμως, απέτυχε καθώς δεν εντοπίστηκε κανένα\n'
                                      'προφίλ χρήστη που να συνδυάζει το δηλωθέν ηλεκτρονικό ταχυδρομείο με "κενό" αριθμό μητρώου!')
            else:
                error_messages.append('Ο αριθμός μητρώου <' + error_data[code]['ece_id'] + '> και '
                                      'το ηλεκτρονικό ταχυδρομείο <' + error_data[code]['email'] + '> που δηλώθηκαν\n'
                                      'είναι μεν καταχωρημένα στη βάση, αλλά η αναζήτηση απέτυχε καθώς δεν εντοπίστηκε '
                                      'κανένα προφίλ χρήστη\n'
                                      'που να τα συνδυάζει!')
            error_codes.append(code)
    return TemplateResponse(request, 'user_profile_recovery.html', {'error_messages': error_messages,
                                                                    'post_validation_error_codes': error_codes})