import datetime
from elasticsearch import NotFoundError
from corehq.apps.userreports.util import get_table_name
from corehq.apps.userreports.adapter import IndicatorAdapter
from corehq.apps.es.es_query import HQESQuery
from corehq.elastic import get_es_new
from dimagi.utils.decorators.memoized import memoized
from pillowtop.es_utils import INDEX_STANDARD_SETTINGS


def normalize_id(values):
    return '-'.join(values).replace(' ', '_')


class ESAlchemy(object):
    def __init__(self, index_name, config):
        self.index_name = index_name
        self.config = config
        self.es = HQESQuery(index_name)

    def __getitem__(self, sliced_or_int):
        hits = self.es[sliced_or_int]
        hits = [self._hit_to_row(hit) for hit in hits]
        if isinstance(sliced_or_int, (int, long)):
            return hits[0]
        return hits

    @property
    @memoized
    def mapping(self):
        # todo: this should use the column configuration instead of mapping
        es = get_es_new()
        mapping = es.indices.get_mapping(index=self.index_name)
        return mapping[self.index_name]['mappings']['indicator']['properties']

    def _hit_to_row(self, hit):
        def mapping_to_datatype(col, value):
            type = self.mapping[col]['type']
            if type == 'date':
                try:
                    return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
                except ValueError:
                    return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
            return value
        return {col: mapping_to_datatype(col, hit[col]) for col in self.column_ordering}

    @property
    @memoized
    def column_ordering(self):
        columns = self.config.indicators.get_columns()
        return [col.database_column_name for col in columns]

    @property
    def column_descriptions(self):
        return [{"name": col} for col in self.column_ordering]

    def count(self):
        return self.es.count()


class IndicatorESAdapter(IndicatorAdapter):

    def __init__(self, config):
        super(IndicatorESAdapter, self).__init__(config)
        self.es = get_es_new()
        self.table_name = get_table_name(config.domain, config.table_id).lower()

    def rebuild_table(self):
        self.drop_table()
        self.es.indices.create(index=self.table_name, body=INDEX_STANDARD_SETTINGS)

    def drop_table(self):
        try:
            self.es.indices.delete(index=self.table_name)
        except NotFoundError:
            # index doesn't exist yet
            pass

    def rebuild_table_if_necessary(self):
        if not self.es.indices.exists(index=self.table_name):
            self.rebuild_table()

    def refresh_table(self):
        self.es.indices.refresh(index=self.table_name)

    def get_query_object(self):
        return ESAlchemy(self.table_name, self.config)

    def best_effort_save(self, doc):
        try:
            self.save(doc)
        except Exception as e:
            self.handle_exception(doc, e)

    def save(self, doc):
        """
        Saves the document. Should bubble up known errors.
        """
        indicator_rows = self.config.get_all_values(doc)
        if indicator_rows:
            es = get_es_new()
            for indicator_row in indicator_rows:
                primary_key_values = [i.value for i in indicator_row if i.column.is_primary_key]
                all_values = {i.column.database_column_name: i.value for i in indicator_row}
                es.index(
                    index=self.table_name, body=all_values,
                    id=normalize_id(primary_key_values), doc_type="indicator"
                )

@@ -849,305 +849,8 @@
 ts%0A%0A
-    @property%0A    @memoized%0A    def mapping(self):%0A        # todo: this should use the column configuration instead of mapping%0A        es = get_es_new()%0A        mapping = es.indices.get_mapping(index=self.index_name)%0A        return mapping%5Bself.index_name%5D%5B'mappings'%5D%5B'indicator'%5D%5B'properties'%5D%0A%0A
     
@@ -912,16 +912,19 @@
 type(col
+umn
 , value)
@@ -941,40 +941,34 @@
     
+data
 type = 
-self.mapping%5Bcol%5D%5B'
+column.data
 type
-'%5D
 %0A   
@@ -979,16 +979,46 @@
      if 
+datatype == 'datetime' or data
 type == 
@@ -1273,16 +1273,17 @@
 n value%0A
+%0A
         
@@ -1294,47 +1294,114 @@
 rn %7B
-col: mapping_to_datatype(col, hit%5Bcol%5D)
+%0A            col.database_column_name: mapping_to_datatype(col, hit%5Bcol.database_column_name%5D)%0A           
  for
@@ -1419,25 +1419,26 @@
 f.column
-_ordering
+s%0A        
 %7D%0A%0A    @
@@ -1450,22 +1450,8 @@
 rty%0A
-    @memoized%0A
     
@@ -1460,25 +1460,17 @@
 f column
-_ordering
+s
 (self):%0A
@@ -1481,17 +1481,14 @@
     
-columns =
+return
  sel
@@ -1520,16 +1520,76 @@
 olumns()
+%0A%0A    @property%0A    @memoized%0A    def column_ordering(self):
 %0A       
@@ -1633,16 +1633,21 @@
  col in 
+self.
 columns%5D

Remove dependency on ES mapping