from datetime import datetime

from pytimeparse.timeparse import timeparse
from jqqb_evaluator.operators import Operators


class Rule:

    def __init__(self, rule_dict):
        self.id = rule_dict['id']
        self.field = rule_dict['field']
        self.type = rule_dict['type']
        self.input = rule_dict['input']
        self.operator = rule_dict['operator']
        self.value = rule_dict['value']

    def evaluate(self, obj):
        return self.get_operator()(self.get_input(obj), self.get_value())

    def get_operator(self):
        return getattr(Operators, 'eval_' + self.operator)

    def get_input(self, obj):
        fields = self.field.split(".")
        result = obj
        steps = len(fields)
        for i in range(steps):
            last_step = i == steps - 1
            second_last_step = i == steps - 2
            result = result.get(fields[i])
            if second_last_step and isinstance(result, list) and isinstance(result[0], dict):
                result = [x[fields[steps - 1]] for x in result]
                break
            result = result[0] if (result is not None and isinstance(result, list) and not last_step) else result
            if result is None:
                break
        if isinstance(result, list):
            return list(map(lambda x: self.typecast_value(x), result))
        else:
            return self.typecast_value(result)

    def get_value(self):
        if isinstance(self.value, list):
            return list(map(lambda x: self.typecast_value(x), self.value))
        return self.typecast_value(self.value)

    def typecast_value(self, value_to_cast):
        if value_to_cast is None:
            return None

        if self.type == 'string':
            return str(value_to_cast)
        elif self.type == 'integer':
            return int(value_to_cast)
        elif self.type == 'double':
            return float(value_to_cast)
        elif self.type == 'date' or self.type == 'datetime':
            return datetime.strptime(value_to_cast, "%Y-%m-%dT%H:%M:%S.%fZ") if isinstance(value_to_cast,
                                                                                           str) else value_to_cast
        elif self.type == 'time':
            return timeparse(value_to_cast) if isinstance(value_to_cast, str) else value_to_cast
        elif self.type == 'boolean':
            if isinstance(value_to_cast, str):
                return value_to_cast.lower() in ['true', '1', 't', 'y', 'yes', 'yeah', 'yup', 'certainly', 'uh-huh']
        return value_to_cast
