#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
#
# Copyright (C) 2017 Bitergia
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Authors:
#   Alvaro del Castillo San Felix <acs@bitergia.com>
#

"""Generates items from ElasticSearch based on filters """


import json
import logging
import requests

from .elk.utils import unixtime_to_datetime, get_repository_filter

logger = logging.getLogger(__name__)


class ElasticItems():
    def __init__(self, perceval_backend, from_date=None, insecure=True, offset=None):

        self.perceval_backend = perceval_backend
        self.last_update = None  # Last update in ocean items index for feed
        self.from_date = from_date  # fetch from_date
        self.offset = offset  # fetch from offset
        self.filter_raw = None  # to filter raw items from Ocean
        self.filter_raw_should = None  # to filter raw items from Ocean

        self.requests = requests.Session()
        if insecure:
            requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
            self.requests.verify = False

        self.elastic = None
        # In large projects like Eclipse commits, 100 is too much
        self.elastic_page = 100
        # self.elastic_page = 10

    def get_repository_filter_raw(self, term=False):
        """ Returns the filter to be used in queries in a repository items """
        perceval_backend_name = self.get_connector_name()
        filter_ = get_repository_filter(self.perceval_backend, perceval_backend_name, term)
        return filter_

    def get_field_date(self):
        """ Field with the update in the JSON items. Now the same in all. """
        return "metadata__updated_on"

    def set_filter_raw(self, filter_raw):
        """ Filter to be used when getting items from Ocean index """
        self.filter_raw = filter_raw

    def set_filter_raw_should(self, filter_raw_should):
        """ Bool filter should to be used when getting items from Ocean index """
        self.filter_raw_should = filter_raw_should

    def get_connector_name(self):
        """ Find the name for the current connector """
        from .utils import get_connector_name
        return get_connector_name(type(self))

    # Items generator
    def fetch(self, query_string=None):
        logger.debug("Creating ocean items generator.")

        elastic_scroll_id = None

        while True:
            rjson = self.get_elastic_items(elastic_scroll_id, query_string)

            if rjson and "_scroll_id" in rjson:
                elastic_scroll_id = rjson["_scroll_id"]

            if rjson and "hits" in rjson:
                if len(rjson["hits"]["hits"]) == 0:
                    break
                for hit in rjson["hits"]["hits"]:
                    eitem = hit['_source']
                    yield eitem
            else:
                logger.error("No results found from %s", self.elastic.index_url)
                break
        return

    def get_elastic_items(self, elastic_scroll_id=None, query_string=None):
        """ Get the items from the index related to the backend """

        if not self.elastic:
            return None
        url = self.elastic.index_url
        # 1 minute to process the results of size items
        # In gerrit enrich with 500 items per page we need >1 min
        # In Mozilla ES in Amazon we need 10m
        max_process_items_pack_time = "10m"  # 10 minutes
        url += "/_search?scroll=%s&size=%i" % (max_process_items_pack_time,
                                               self.elastic_page)

        if elastic_scroll_id:
            """ Just continue with the scrolling """
            url = self.elastic.url
            url += "/_search/scroll"
            scroll_data = {
                "scroll" : max_process_items_pack_time,
                "scroll_id" : elastic_scroll_id
                }
            r = self.requests.post(url, data=json.dumps(scroll_data))
        else:
            # If using a perceval backends always filter by repository
            # to support multi repository indexes
            # We need the filter dict as a string to join with the rest
            filters = self.get_repository_filter_raw(term=True)
            filters = json.dumps(filters)

            if self.filter_raw:
                filters += '''
                    , {"term":
                        { "%s":"%s"  }
                    }
                ''' % (self.filter_raw['name'], self.filter_raw['value'])

            if self.from_date:
                date_field = self.get_field_date()
                from_date = self.from_date.isoformat()

                filters += '''
                    , {"range":
                        {"%s": {"gte": "%s"}}
                    }
                ''' % (date_field, from_date)
            elif self.offset:
                filters += '''
                    , {"range":
                        {"offset": {"gte": %i}}
                    }
                ''' % (self.offset)

            if query_string:
                filters += """
                , { "query_string": {
                    "fields" : ["%s"],
                    "query": "%s"
                    }
                }
                """ % (query_string['fields'], query_string['query'])


            # Order the raw items from the old ones to the new so if the
            # enrich process fails, it could be resume incrementally
            order_query = ''
            order_field = None
            if self.perceval_backend:
                order_field = 'metadata__timestamp'
            elif self.get_connector_name() == 'twitter':
                order_field = '@timestamp'
            if order_field is not None:
                order_query = ', "sort": { "%s": { "order": "asc" }} ' % order_field

            filters_should = ''
            if self.filter_raw_should:
                filters_should = json.dumps(self.filter_raw_should)[1:-1]
                # We need to add a bool should query to the outer must query
                query_should = '{"bool": {%s}}' % filters_should
                filters += ", " + query_should

            query = """
            {
                "query": {
                    "bool": {
                        "must": [%s]
                    }
                } %s
            }
            """ % (filters, order_query)

            logger.debug("%s\n%s", url, json.dumps(json.loads(query), indent=4))
            r = self.requests.post(url, data=query)

        items = []
        rjson = None
        try:
            rjson = r.json()
        except:
            logger.error("No JSON found in %s" % (r.text))
            logger.error("No results found from %s" % (url))

        return rjson
