import pandas as pd
import numpy as np
from datetime import datetime
import time

class EzCheck:
    def __init__(self):
        self.prep_data='EzCheck'

    def get_null(self, x):
        res = x.isna().sum()
        return res

    def get_dtypes(self, x):
        res = x.dtypes
        return res

    def get_nunique(self, x):
        res = x.nunique()
        return res

    def get_outlier(self, data):
        columns = data.columns.values
        res = []
        for col in columns:
            if data[col].dtypes.name in ['int8','int16','int32','int64',
                                           'float16','float32','float64']:
                q1,q3 = data[col].quantile([.25,.75])
                IQR = (q3-q1)*1.5
                lower_bound = q1-IQR
                upper_bound = q3+IQR

                if (data[col] < lower_bound).sum() != 0 | (data[col] > upper_bound).sum() != 0:
                    res.append('yes')
                else:
                    res.append('no')
            else:
                res.append('undefined')
        return res

    def convert_dtypes(self, data,column):
        pass

    def check(self, data, n=4):
        check_df = pd.DataFrame()
        data = data
        n_row, n_col = data.shape
        pipeline = {'dtypes':self.get_dtypes,'missing':self.get_null,
                    'missing_ratio':self.get_null,
                    'nunique':self.get_nunique,
                    'n_ratio':self.get_nunique}
        for col, func in pipeline.items():
            if 'ratio' not in col:
                check_df[col] = func(data)
            else:
                check_df[col] = func(data)/n_row

        check_df['outlier'] = self.get_outlier(data)

        return check_df.round(n)

    def get_num(self,x):
        arr = pd.Series([x.count(),min(x),x.quantile(.25),x.median(),x.mean(),
                         x.quantile(.75),max(x),max(x)-min(x),x.skew(),x.std()], 
                        index=['count','min','25%','median','mean','75','max','range','skew','std'])
        return arr

    def get_obj(self,x):
        arr = pd.Series([x.count(),x.value_counts().index[0],x.value_counts().values[0],
                         x.value_counts().index[-1],x.value_counts().values[-1]],
                        index=['count','top','t_freq','least','l_freq'])
        return arr

    def get_date(self,x):
        arr = pd.Series([x.count(),x.min(),x.max(), x.max()-x.min()],
                        index=['count','min','max','day'])
        return arr

    def summary(self, data, method):
        data = data.select_dtypes(method)
        columns = data.columns
        summary_df = pd.DataFrame()
        for col in columns:

            if method=='number':
                summary_df[col] = self.get_num(data[col])
                
            elif method=='object':
                summary_df[col] = self.get_obj(data[col])

            elif method=='datetime':
                summary_df[col] = self.get_date(data[col])

        return summary_df.T.round(4)

    def sumNum(self, data, method='number'):
        return self.summary(data, method)

    def sumCat(self, data, method='object'):
        return self.summary(data, method)

    def sumDate(self, data, method='datetime'):
        return self.summary(data, method)