import gzip
import collections
from flask import make_response, json, current_app
import numpy as np
import pandas as pd
from app.errors import *
import gc
from app import db
from app.models import Validation
import yaml
import requests
from app.main.utils.db_util import return_obj
from sqlalchemy import create_engine
from collections.abc import MutableMapping


def response_message(data=None, status=200):
    if status != 200:
        try:
            error = {'fa': data.message_fa, 'en': data.message_en}
        except AttributeError:
            print(data)
            error = MethodError()
            error = {'message_fa': error.message_fa, 'message_en': error.message_en}
        resp_dict = {'message': error}
    else:
        resp_dict = data
    resp_dict = json.loads(json.dumps(resp_dict, default=convert, sort_keys=False).replace('"None"', 'null')
                           .replace('NaN', 'null'), strict=False)
    content = gzip.compress(json.dumps(resp_dict, ensure_ascii=False, indent=3, default=convert,
                                       sort_keys=False).encode('utf8'), 5)
    response = make_response(content, status)
    response.headers['Content-length'] = len(content)
    response.headers['Content-Encoding'] = 'gzip'
    response.headers['Content-Type'] = 'application/json; charset=utf-8'
    response.headers.add('Access-Control-Allow-Origin', '*')
    gc.collect()
    return response


def deep_dict_update(d, u):
    for k, v in u.items():
        if isinstance(v, collections.abc.Mapping):
            d[k] = deep_dict_update(d.get(k, {}), v)
        else:
            d[k] = v
    return d


def convert(o):
    if isinstance(o, np.int64):
        return int(o)
    if isinstance(o, np.bool_):
        if o:
            return True
        else:
            return False
    if pd.isna(o):
        return None
    if o is np.ma.masked:
        return None
    json.dumps(o)


def read_data(dsid):
    dataset = return_obj(dsid, 'ds', to_dict=False)
    if dataset.data['type'] == 'rp':
        uid = dataset.data['uid']
        req = requests.post(f'{current_app.config["IO"]}/get_data/{uid}', json={'form_code': 'all', 'where': 'rules'})
        return pd.DataFrame(req.json()['data'])
    if dataset.data['type'] == 'db':
        query = dataset.data['query']
        conn = dataset.data['connection_string']
        engine = create_engine(conn)
        return pd.read_sql(query, engine)


def read_rules(rgid=None, where='file', path=None):
    if where == 'db':
        if rgid:
            return return_obj(rgid, 'rg')
    else:
        if not path:
            path = 'app/main/static/rule_config.yml'
        return yaml.load(open(path), Loader=yaml.FullLoader), None


def write_validations(data, where='file', path=None):
    if where == 'db':
        # try:
            # data = data.loc[~data['validation']]
        obj = []
        data.apply(lambda x: obj.append(Validation(rid=x['rid'], rgid=x['rgid'], modified_date=x['modified_date'],
                                                   validated=x['validated'], responsible=x['responsible'],
                                                   created_date=x['created_date'], dsid=x['dsid'])), axis=1)
        db.session.bulk_save_objects(obj)
        db.session.commit()
        # except Exception as e:
        #     return str(e)
    else:
        try:
            if not path:
                path = 'validation.xlsx'
            data.to_excel(path)
        except Exception as e:
            return str(e)
    return 'success'
