from django import http
from django.db.models import Q
from django.db.transaction import non_atomic_requests
from django.utils import translation
from django.utils.encoding import force_bytes
from django.utils.translation import ugettext
from django.views.decorators.vary import vary_on_headers

import waffle

import olympia.core.logger
from olympia import amo
from olympia.bandwagon.views import get_filter as get_filter_view
from olympia.browse.views import personas_listing as personas_listing_view
from olympia.addons.indexers import WEBEXTENSIONS_WEIGHT
from olympia.addons.models import Addon, Category
from olympia.amo.decorators import json_view
from olympia.amo.helpers import locale_url, urlparams
from olympia.amo.utils import sorted_groupby, render
from olympia.bandwagon.models import Collection
from olympia.versions.compare import dict_from_int, version_dict, version_int

from .forms import ESSearchForm, SecondarySearchForm
from .filters import get_locale_analyzer


DEFAULT_NUM_COLLECTIONS = 20
DEFAULT_NUM_PERSONAS = 21  # Results appear in a grid of 3 personas x 7 rows.

log = olympia.core.logger.getLogger('z.search')


def _personas(request):
    """Handle the request for persona searches."""

    initial = dict(request.GET.items())

    # Ignore these filters since return the same results for Firefox
    # as for Thunderbird, etc.
    initial.update(appver=None, platform=None)

    form = ESSearchForm(initial, type=amo.ADDON_PERSONA)
    form.is_valid()

    qs = Addon.search_public()
    filters = ['sort']
    mapping = {'downloads': '-weekly_downloads',
               'users': '-average_daily_users',
               'rating': '-bayesian_rating',
               'created': '-created',
               'name': 'name_sort',
               'updated': '-last_updated',
               'hotness': '-hotness'}
    results = _filter_search(request, qs, form.cleaned_data, filters,
                             sorting=mapping, types=[amo.ADDON_PERSONA])

    form_data = form.cleaned_data.get('q', '')

    search_opts = {}
    search_opts['limit'] = form.cleaned_data.get('pp', DEFAULT_NUM_PERSONAS)
    page = form.cleaned_data.get('page') or 1
    search_opts['offset'] = (page - 1) * search_opts['limit']

    pager = amo.utils.paginate(request, results, per_page=search_opts['limit'])
    categories, filter, base, category = personas_listing_view(request)
    c = dict(pager=pager, form=form, categories=categories, query=form_data,
             filter=filter, search_placeholder='themes')
    return render(request, 'search/personas.html', c)


def _collections(request):
    """Handle the request for collections."""

    # Sorting by relevance isn't an option. Instead the default is `weekly`.
    initial = dict(sort='weekly')
    # Update with GET variables.
    initial.update(request.GET.items())
    # Ignore appver/platform and set default number of collections per page.
    initial.update(appver=None, platform=None, pp=DEFAULT_NUM_COLLECTIONS)

    form = SecondarySearchForm(initial)
    form.is_valid()

    qs = Collection.search().filter(listed=True, app=request.APP.id)
    filters = ['sort']
    mapping = {'weekly': '-weekly_subscribers',
               'monthly': '-monthly_subscribers',
               'all': '-subscribers',
               'rating': '-rating',
               'created': '-created',
               'name': 'name_sort',
               'updated': '-modified'}
    results = _filter_search(request, qs, form.cleaned_data, filters,
                             sorting=mapping,
                             sorting_default='-weekly_subscribers',
                             types=amo.COLLECTION_SEARCH_CHOICES)

    form_data = form.cleaned_data.get('q', '')

    search_opts = {}
    search_opts['limit'] = form.cleaned_data.get('pp', DEFAULT_NUM_COLLECTIONS)
    page = form.cleaned_data.get('page') or 1
    search_opts['offset'] = (page - 1) * search_opts['limit']
    search_opts['sort'] = form.cleaned_data.get('sort')

    pager = amo.utils.paginate(request, results, per_page=search_opts['limit'])
    c = dict(pager=pager, form=form, query=form_data, opts=search_opts,
             filter=get_filter_view(request),
             search_placeholder='collections')
    return render(request, 'search/collections.html', c)


class BaseAjaxSearch(object):
    """Generates a list of dictionaries of add-on objects based on
    ID or name matches. Safe to be served to a JSON-friendly view.

    Sample output:
    [
        {
            "id": 1865,
            "name": "Adblock Plus",
            "url": "http://path/to/details/page",
            "icons": {
                "32": "http://path/to/icon-32",
                "64": "http://path/to/icon-64"
            }
        },
        ...
    ]

    """

    def __init__(self, request, excluded_ids=(), ratings=False):
        self.request = request
        self.excluded_ids = excluded_ids
        self.src = getattr(self, 'src', None)
        self.types = getattr(self, 'types', amo.ADDON_TYPES.keys())
        self.limit = 10
        self.key = 'q'  # Name of search field.
        self.ratings = ratings

        # Mapping of JSON key => add-on property.
        default_fields = {
            'id': 'id',
            'name': 'name',
            'url': 'get_url_path',
            'icons': {
                '32': ('get_icon_url', 32),
                '64': ('get_icon_url', 64)
            }