#!/usr/bin/env python3

"""
Cuchulufm is a series of wrappers for bottle, mako and others and construct a simple headless cms.

Copyright (C) 2024 Antonio de la Rosa Caballero

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

from cuchulu.libraries.db import corefields
from cuchulu.libraries.db.coreforms import PasswordForm
from cuchulu.libraries.i18n import I18n
from cuchulu.libraries.sessionplugin import get_session
from cuchulu.libraries.keyutils import create_key_encrypt
from bottle import request, abort

# Need unittest

"""Functions and classes for process forms"""

def pass_values_to_form(post, arr_form, yes_error=True, pass_values=True):
    """Function for pass a dict with form values for check using forms dict
    
    Values dict and Forms dict need have the same key. A forms dict is maked of a serie of cuchulu2 forms elements, used for check the value.
    
    Args:
        post (dict): Dict composed by a series of values. The keys need to be equal to keys of arr_form dict.
        arr_form (dict): Dict composed by a series or forms objects. The keys need to be equal to keys of post dict.
        yes_error (bool): Show errors in txt_error form variables. 
        pass_values (bool): Pass default values or values from post dict to arr_form dict items
    
    Returns:
        arr_form (dict): Return arr_form dict with checked values from post dict.
    
    """
    
    if pass_values:
        def get_value(key):
            return post[key]            
            
    else:
        def get_value(key):
            return arr_form[key].default_value            
                
    for key, value in arr_form.items():
        
        post[key]=post.get(key, '')
        
        #if arr_form[key].default_value=='':
        arr_form[key].default_value=get_value(key)
        
        if arr_form[key].field==None:
            arr_form[key].field=corefields.CharField(key, 255, required=False) 
        
        # Recheck value if no set error field
        if arr_form[key].field.error == None:
            arr_form[key].field.check(post[key])
        
        #arr_form[key].txt_error=""
        
        if arr_form[key].required==True and arr_form[key].field.error==True and yes_error==True:
           arr_form[key].txt_error=arr_form[key].field.txt_error

        # Reset error on field. 

        arr_form[key].field.error=None

    return arr_form
    
class CheckForm():
    """Simple class for make similar check to pass_values_to_form. More simple.
    """
    
    def __init__(self):
        
        self.error=0

    def check(self, post, arr_form):
        
        """Simple method for pass a dict with form values for check using forms dict
    
        Values dict and Forms dict need have the same key. A forms dict is maked of a serie of cuchulu2 forms elements, used for check the value.
        
        Args:
            post (dict): Dict composed by a series of values. The keys need to be equal to keys of arr_form dict.
            arr_form (dict): Dict composed by a series or forms objects. The keys need to be equal to keys of post dict.
        
        Returns:
            post (dict): Return post dict with values checked
            arr_form (dict): Return arr_form with errors and values. 
        
        """
        
        for k in arr_form.keys():
            
            post[k]=post.get(k, '')
            
            if arr_form[k].field==None:
               arr_form[k].field=corefields.CharField(k, 255, required=False) 
            
            post[k]=arr_form[k].field.check(post[k])
            arr_form[k].txt_error=arr_form[k].field.txt_error
            
            if arr_form[k].field.error==True and arr_form[k].required==True:
                self.error+=1
        
        return post, arr_form
        
def check_form(post, arr_form, sufix_form='_error'):
    
    """Function for make check form, passing errors to extra dict called error_form. Also returns an bool variable setting error. 
    
    Args:
        post (dict): Dict composed by a series of values. The keys need to be equal to keys of arr_form dict.
        arr_form (dict): Dict composed by a series or forms objects. The keys need to be equal to keys of post dict.
        sufix_form (str): Define the sufix of error_form keys
        
    Returns:
        error (bool): Return False if not errors in checking, if errors return True
        error_form (dict): A dict containing the errors in form fields.
        post (dict): Sanitized values 
        arr_form (dict): arr_form with errors and values. 
    
    """
    
    error=0
    error_form={}
    
    for k in arr_form.keys():
        
        post[k]=post.get(k, '')
        
        if arr_form[k].field==None:
           arr_form[k].field=corefields.CharField(k, 255, required=False) 
        
        post[k]=arr_form[k].field.check(post[k])
        arr_form[k].txt_error=arr_form[k].field.txt_error
        
        if arr_form[k].field.error==True and arr_form[k].required==True:
            error_form['#'+k+sufix_form]=arr_form[k].txt_error
            error+=1
    
    return error, error_form, post, arr_form

def show_form(post, arr_form, t, yes_error=True, pass_values=True, modelform_tpl='forms/modelform.phtml'):
        
    # Create csrf_token in session
    """Function for generate a html form from a template
    
    Args:
        post (dict): Dict composed by a series of values. The keys need to be equal to keys of arr_form dict.
        arr_form (dict): Dict composed by a series or forms objects. The keys need to be equal to keys of post dict.
        t (PTemplate): Object used for load template for form
        yes_error (bool): Show errors in txt_error form variables. 
        pass_values (bool): Pass default values or values from post dict to arr_form dict items
        modelform_tpl (str): Path for the template that generate the html form. By default is cuchulu2/libraries/templates/forms/modelform.phtml
    
    Returns:
    
        template (str): An html string with the generated form.
    
    """    
    
    
    generate_csrf()
    
    if pass_values==True:
        pass_values_to_form(post, arr_form, yes_error, pass_values)
    
    return t.load_template(modelform_tpl, forms=arr_form)

def extract_post(post, fields):
    """Helper function for create a simple array from other using fields list for filter
    
    Args:
        post (dict): A dict with keys and values to filter.
        fields (list): A list with keys to validate.
    """
    
    return {k:v for k,v in post.items() if k in fields}

#Simple Function for add repeat_password form to user model

def set_extra_forms_user(user_admin):
    """Helper function for add extraforms to UserModel form, not for general use
    
    Args:
        user_admin (UserModel): Instance of UserModel object for modify forms and fields
    
    """
    
    user_admin.fields['password'].required=True
    user_admin.fields['email'].required=True

    user_admin.create_forms(['username', 'email', 'password'])
    
    user_admin.forms['repeat_password']=PasswordForm('repeat_password', '')
    
    user_admin.forms['repeat_password'].required=True
    
    user_admin.forms['repeat_password'].label=I18n.lang('common', 'repeat_password', 'Repeat Password')

#Function for initial values for necessary fields.

def ini_fields(fields):
    pass

def csrf_token(token_id='csrf_token'):
    
    """Function for generate a csrf token html hide form using sessions plugin
    
    Args:
        token_id (str): Name of the html hide form
        
    Returns:
        html (str): Return html input hidden with csrf token saved in session
    """
    
    s=get_session()
    
    #if not 'csrf_token' in s:    
    s['csrf_token']=create_key_encrypt()
    
        #s.save()
    
    return '<input type="hidden" name="csrf_token" class="csrf_token" id="'+token_id+'" value="'+s['csrf_token']+'" />'
    
def generate_csrf():
    
    """Function for generate a csrf token in a variable
    
    Returns:
        csrf_token (str): csrf token value
    """
    
    s=get_session()
    
    if not 'csrf_token' in s:    
        s['csrf_token']=create_key_encrypt()
        #s.save()

    return s['csrf_token']

def request_type():
    
    """Simple shortcut for get the request_type"""
    
    return request.environ['REQUEST_METHOD']
    
def check_csrf(name_csrf_token='csrf_token'):
    
    """Function for check the csrf token
    
    Args:
        name_csrf_token (str): Name of the csrf_token in session
    
    """
    
    session=get_session()
    
    csrf_token=session.get(name_csrf_token, '')
    
    if csrf_token=='' or csrf_token!=request.forms.get(name_csrf_token):
        abort(403, 'You need a valid csrf token')
        
    
