#!/usr/bin/env python3

from collections import OrderedDict
import sys

def query(model, str_query, args=[], connection_id='default'):
        
    model.connect_to_db()
    
    return model.sqlclass.query(str_query, args, connection_id)

# Insert method, for insert a row in database.using a dictionary
# External agent define if the update is in code or from external source, how a form.

def insert(model, dict_values, external_agent=True):
    
    model.clean_fields()
    
    # Connect to db

    model.post=dict_values

    #model.connect_to_db()
    
    query_error=False
    last_sql=''
    
    #model.fields[model.name_field_id].required=False
    
    if model.name_field_id in dict_values:
        del dict_values[model.name_field_id]
    
    try:
        
        fields, values, update_values=model.check_all_fields(dict_values, external_agent)
        
    except: 

        query_error=(model.sqlclass.error_connection+' '+sys.exc_info()[0], '')
        #cursor.close()
        return (query_error, False)

    
    c=len(values)
    
    arr_str=['%s' for x in range(c)]
    
    sql="insert into `"+model.name+"` (`"+"`, `".join(fields)+"`) VALUES ("+", ".join(arr_str)+")"
    
    last_sql=sql
    
    cursor=model.query(sql, values, model.connection_id)
    
    if cursor.rowcount>0:
        
        model.last_id=cursor.lastrowid
        
        cursor.close()
        
        # Delete cache for this table.
        
        return (False, True)
    else:
        query_error=('Cannot insert the new row', last_sql)
        
        
        return (query_error, False)

class QueryBuilderException(Exception):
    pass
    
def select(model, conditions=['', []], arr_select=[], raw_query=False):
    
    model.clean_fields()

    final_fields=[]
        
    extra_fields=[]
    
    #model.query_error=''
    query_error=False
    last_query=''
    
    #First table selecction
    
    tables_to_select=['`'+model.name+'`']
    
    keys=list(model.fields.keys())
    
    if len(arr_select)==0:
        arr_select=keys
    
    # Array intersect for obtain the valid fields
    
    fields =  list(set(keys) & set(arr_select))
    
    #Creating the fields
    arr_repeat_field={}
    
    new_fields=OrderedDict()
    for field in fields:
        
        #Check if foreignkeyfield
        
        if type(model.fields[field]).__name__=="ForeignKeyField" and raw_query==False:
            
            if model.fields[field].table_name in arr_repeat_field:
                arr_repeat_field[model.fields[field].table_name]+=1
                
            else:
                arr_repeat_field[model.fields[field].table_name]=0
            
            table_name=model.fields[field].table_name+'` as `'+model.fields[field].table_name+str(arr_repeat_field[model.fields[field].table_name])
            
            final_table_name=model.fields[field].table_name+str(arr_repeat_field[model.fields[field].table_name])
            
            # The name with its alias of this related table model
            
            tables_to_select.append('`'+table_name+'`')
            
            # Add field from related table
            # as "+table_name+"_"+model.fields[field].named_field
            extra_fields.append("`"+final_table_name+"`.`"+model.fields[field].named_field+"` as "+field)
            
            # Add a condition to sql query for join the two tables.
            
            conditions[0]+=" AND `"+final_table_name+"`.`"+model.fields[field].identifier_field+"`=`"+model.name+"`.`"+field+"`"
            
            # Add extra fields from related table from select_fields ForeignKeyField class member
            
            select_fields=model.fields[field].select_fields
            
            for extra_field in select_fields:
                
                model.fields[field+'_'+extra_field]=model.fields[field].related_model.fields[extra_field]
                model.fields_to_clean.append(field+'_'+extra_field)
                
                # Check if extra_field is ForeignKeyField, if yes, call this function recursively.
            
                extra_fields.append("`"+final_table_name+"`.`"+extra_field+"` as `"+field+"_"+extra_field+"`")
        else:
            # Add normal field to sql query
            
            final_fields.append("`"+model.name+"`.`"+field+"`")
    
    #if len(new_fields)>0:
        #model.fields.update(new_fields)
    
    extra_sql_field=""
    
    if len(extra_fields)>0:
        
        extra_sql_field=", "+", ".join(extra_fields)
        
    if len(final_fields)==0:
        query_error=("Error: without fields to search", '')
        #return (query_error, False)
        raise QueryBuilderException("Error: without fields to search")
    
    sql= ("select "+" "+model.distinct+", ".join(final_fields)+extra_sql_field+" from "+", ".join(tables_to_select)+' '+conditions[0]).strip()
    
    last_query=sql
    
    cursor=model.query(sql, conditions[1], model.connection_id)
    
    if cursor==False:
        #query_error=(model.sqlclass.error_connection, last_query)
        #cursor.close()
        #return (query_error, False)
        raise QueryBuilderException(model.sqlclass.error_connection+last_query)
    else:
        return cursor

def select_to_array(model, conditions=['', []], fields_selected=[], raw_query=0):
        
    if len(fields_selected)==0:
        fields_selected=model.fields.keys()
    
    if (model.name_field_id not in fields_selected):
        fields_selected.append(model.name_field_id)
        def del_row_id(row):
            
            try:
            
                index_id=row.index(model.name_field_id)
                
                del row[index_id]
                
            except:
                
                pass
    else:
        def del_row_id(row):
            pass
    
    results=[] #OrderedDict()
    
    with select(model, conditions, fields_selected, raw_query) as cursor:        
        for row in cursor:
            
            if model.show_formatted and row:
                for k, col in row.items():
                    if model.fields[k].show_formatted_value:
                        row[k]=self.fields[k].show_formatted(col)
            
            results.append(row)
        
        del_row_id(results)
    
    return results

def select_to_dict(model, conditions=['', []], fields_selected=[], raw_query=0, integer_dict=False):    
    
    if not integer_dict:
        def conv_int(i):
            return str(i)
    else:
        def conv_int(i):
            return i        
    
    if len(fields_selected)==0:
        fields_selected=model.fields.keys()
    
    if (model.name_field_id not in fields_selected):
        fields_selected.append(model.name_field_id)
        def del_row_id(row):
            
            try:
            
                index_id=row.index(model.name_field_id)
                
                del row[index_id]
                
            except:
                
                pass
    else:
        def del_row_id(row):
            pass
    
    results=OrderedDict()
    
    with select(model, conditions, fields_selected, raw_query) as cursor:        
        for row in cursor:

            if model.show_formatted and row:
                for k, col in row.items():
                    row[k]=model.fields[k].show_formatted(col)

            results[conv_int(row[model.name_field_id])]=row
        
        del_row_id(results)
    
    return results

def select_a_row_where(model, conditions=['', []], fields_selected=[], raw_query=0, begin=0):
    
    limit="limit "+str(begin)+", 1"
    
    with select(model, conditions, fields_selected, raw_query) as cursor:
    
        row=cursor.fetchone()
        
        if row==None:
            row=False
        else:
            if model.show_formatted:
                for k, col in row.items():
                    row[k]=model.fields[k].show_formatted(col)
    
    return row

def select_a_row(model, id, fields_selected=[], raw_query=0):
    
    conditions=['WHERE `'+model.name+'`.`'+model.name_field_id+'`=%s', [id]]
    
    with select(model, conditions, fields_selected, raw_query) as cursor:
    
        row=cursor.fetchone()
        
        if row==None:
            row=False
        else:
            if model.show_formatted:
                for k, col in row.items():
                    row[k]=model.fields[k].show_formatted(col)
    return row


# A method por count num rows affected for sql conditions

def select_count(model, conditions=['', []], field_to_count='id', raw_query=True):
    
    print(model.dummy)
    #First table selecction
    
    tables_to_select=['`'+model.name+'`']
    
    fields=list(model.fields.keys())
    
    #Creating the fields
    
    for field in fields:
        
        #Check if foreignkeyfield
        
        if type(model.fields[field]).__name__=="ForeignKeyField"  and raw_query==False:
            
            table_name=model.fields[field].table_name
            
            tables_to_select.append('`'+table_name+'`')
            
            # Add a condition to sql query for join the two tables.
            
            conditions[0]+=" AND `"+table_name+"`.`"+model.fields[field].identifier_field+"`=`"+model.name+"`.`"+field+"`"
            
    sql= "select count(`"+field_to_count+"`) from "+", ".join(tables_to_select)+' '+conditions[0]
    
    count=0
    
    with model.query(sql, conditions[1], model.connection_id) as cursor:
        count=list(cursor.fetchone().values())[0]
        
        if model.yes_reset_conditions:
            model.reset_conditions()
    
    return count

