# (c) cavaliba.com - data - loader.py

import os
import yaml
import json
import csv

from app_home.configuration import get_configuration

from app_data.data import load_schema
from app_data.data import load_instance

from app_user.user import load_user
from app_user.permission import load_permission
from app_user.group import load_group
from app_user.role import load_role

from app_home.load import load_home

from app_data.pipeline import get_pipeline




# ---------------------------------------------------------------------
# Load broker for one or more objects provided as a LIST of DICTs
# ---------------------------------------------------------------------
def load_broker(datalist=None, aaa=None, force_action=None):

    '''
    Load a batch of object into the database

    IN:
       datalist: [ {}, {}, {}, ... ] 
       aaa:
       force_action:
    
    OUT:
       reply struct (dict) : count, count_ok, count_ko, errors=[]
    '''

    reply = {
        'count': 0,
        'count_ok': 0,
        'count_ko': 0,
        'errors':  [],
    }

    if not datalist:
        reply['errors'].append("no data provided")
        return reply
        
    if not type(datalist) is list:
        reply['errors'].append("invalid data provided (not a list)")
        return reply

    if not aaa:
        reply['errors'].append("missing identity")
        return reply
    
    if 'perms' not in aaa:
        reply['errors'].append("missing aaa permissions")
        return reply


    for datadict in datalist:
        
        err = None

        reply['count'] += 1

        if not type(datadict) is dict:
            reply['count_ko'] += 1
            reply['errors'].append(f"invalid entry: {datadict}")
            continue

        classname = datadict.get("classname", None)
        if not classname:
            reply['count_ko'] += 1
            reply['errors'].append(f"missing classname in {datadict}")
            continue

        if len(classname) == 0:
            # unknown object class : NEXT : log error
            reply['count_ko'] += 1
            reply['errors'].append(f"invalid classname in {datadict}")
            continue
        
        if force_action:
            datadict["_action"] = force_action

        if classname == "_user":
            err = load_user(datadict=datadict, aaa=aaa)

        elif classname == "_schema":
            err = load_schema(datadict=datadict, aaa=aaa)

        elif classname == "_group":
            err = load_group(datadict=datadict, aaa=aaa)

        elif classname == "_role":
            err = load_role(datadict=datadict, aaa=aaa)

        elif classname == "_permission":
            err = load_permission(datadict=datadict, aaa=aaa)            

        elif classname == "_home":
            err = load_home(datadict=datadict, aaa=aaa)

        else:
            # internal or user-defined Instance
            err = load_instance(datadict=datadict, aaa=aaa)

        if err:
            reply['count_ko'] += 1
            reply['errors'].append(err)
        else:
            reply['count_ok'] += 1

    return reply

# ---------------------------------------------------------------------
# CSV File to DATA
# ---------------------------------------------------------------------

# # custom processing of CSV file with header line and composit field
# def make_dictlist_from_csv(filedata,  splitfields=[]):

#     lines = filedata.splitlines()
#     header = []
#     rows = []
#     line_count = 0

#     delimiter = get_configuration(appname="home", keyname="CSV_DELIMITER") 

#     for line in lines:
#         entry = {}                    
#         fields = line.split(delimiter)
#         if line_count == 0:
#             header = fields
#             line_count = 1
#         else:
#             line_count += 1
#             for i in range(0,len(header)):
#                 hi = header[i]
#                 if hi in splitfields:
#                     try:
#                         a = fields[i].split("+")
#                         entry[hi] = a
#                     except:
#                         pass
#                 else:
#                     try:
#                         entry[hi] = fields[i]
#                     except:
#                         pass
#             rows.append(entry)        

#     return rows


def load_file_csv(file=None, filename=None, pipeline=None, verbose=None, first=1, last=0):
    ''' 
    pipeline = keyname of pipeline object

    output: list of dict = [ {} , {}, {} ... ] 
    {
        classname: _home|_user|_group|_role|_permisison|_schema|_enumerate|_dataview|_pipeline| ...
            _sirene_category|_sirene_public|_sirene_template
            CLASSNAME
        keyname: my key
        displayname: "a nice display name"
        is_enabled: 
        (...)
    }
    '''


    if not pipeline:
        print("SKIP - missing pipeline")
        return
    
    pipeline_data = get_pipeline(pipeline)
    if not pipeline_data:
        print("SKIP - invalid pipeline")
        return

    classname = pipeline_data.get("classname", None)
    if not classname:
        print("SKIP - missing classname")
        return

    keyfield = pipeline_data.get("keyfield", "keyname")

    csv_delimiter = pipeline_data.get("csv_delimiter", None)
    if not csv_delimiter:
        csv_delimiter = get_configuration(appname="home", keyname="CSV_DELIMITER")

    encoding = pipeline_data.get("encoding", "utf-8")


    datalist = []


    # from file
    if file:

        csv_reader = csv.DictReader(file, delimiter=csv_delimiter)

        cursor = 0

        for entry in csv_reader:

            cursor += 1
            if cursor < first:
                continue
            if last > 0:
                if cursor > last:
                    break

            keyname = entry.get(keyfield, None)                
            if keyname:
                entry["classname"] = classname
                entry["keyname"] = keyname
                datalist.append(entry)
            else:
                print("    !skip entry (no keyname/keyfield)")
                
    # from filename
    else:

        if not os.path.isfile(filename):
            print(f"SKIP - not a file ({filename})")
            return

        with open(filename, encoding=encoding) as file:

            # NEXT : handle CSV without header line (use pipeline structure)
            # filedata = file.read().decode("utf-8")

            csv_reader = csv.DictReader(file, delimiter=csv_delimiter)

            cursor = 0

            for entry in csv_reader:

                cursor += 1
                if cursor < first:
                    continue
                if last > 0:
                    if cursor > last:
                        break

                keyname = entry.get(keyfield, None)                
                if keyname:
                    entry["classname"] = classname
                    entry["keyname"] = keyname
                    datalist.append(entry)
                else:
                    print("    !skip entry (no keyname/keyfield)")


    # cleanup empty field
    # for entry in datalist:
    #     for fieldname, fielddata in entry.items():
    #         print("**** ", fieldname, fielddata)


    return datalist

# ---------------------------------------------------------------------
# JSON to DATA
# ---------------------------------------------------------------------

def load_file_json(file=None, filename=None, pipeline=None, verbose=None):

    data = []

    # if pipeline needed during load (NEXT)
    pipeline_data = {}
    if pipeline:
        pipeline_data = get_pipeline(pipeline)
    if not pipeline_data:
        pipeline_data = {}


    # from file
    if file:
        try:
            data = json.load(file)
        except Exception as e:
            print(f"SKIP - JSON syntax error in file : {e}")
            return

    # from filename
    else:
        if not filename:
            print("SKIP - missing filename")
            return

        if not os.path.isfile(filename):
            print(f"SKIP - not a file ({filename})")
            return

        # if not filename.endswith('.json'):
        #     print(f"SKIP - not a JSON file ({filename})")
        #     return
                        
        with open(filename) as file:
            data = json.load(file)

    if not type(data) is list:
        print(f"SKIP - invalid JSON, not a LIST ({filename})")
        return

    return data

# ---------------------------------------------------------------------
# YAML File to DATA
# ---------------------------------------------------------------------
# from file, default from filename
# Second, try filename

def load_file_yaml(file=None, filename=None, pipeline=None, verbose=None):

    data = []

    # if pipeline needed during load (NEXT)
    pipeline_data = {}
    if pipeline:
        pipeline_data = get_pipeline(pipeline)
    if not pipeline_data:
        pipeline_data = {}
        
    # from file
    if file:
        try:
            data = yaml.load(file, Loader=yaml.SafeLoader)
        except Exception as e:
            print("SKIP - YAML syntax error in file : ", e)
            return
    
    # from filename
    else:
        if not filename:
            print("SKIP - missing filename")
            return

        if not os.path.isfile(filename):
            print(f"SKIP - not a file ({filename})")
            return

        # if not (filename.endswith('.yml') or filename.endswith('.yaml')):
        #     print(f"SKIP - not a YAML file ({filename})")
        #     return

        with open(filename) as f:
            try:
                data = yaml.load(f, Loader=yaml.SafeLoader)
            except Exception as e:
                print(f"SKIP - YAML syntax error in {filename} : ", e)

    if not type(data) is list:
        print(f"SKIP - invalid YAML, not a LIST ({filename})")
        return

    return data

