import json
import os
import logging
import requests

from satstac import Collection, Item, ItemCollection
from satstac.utils import dict_merge
from urllib.parse import urljoin

logger = logging.getLogger(__name__)


class SatSearchError(Exception):
    pass


class Search(object):
    """ One search query (possibly multiple pages) """
    search_op_list = ['>=', '<=', '=', '>', '<']
    search_op_to_stac_op = {'>=': 'gte', '<=': 'lte', '=': 'eq', '>': 'gt', '<': 'lt'}

    def __init__(self, url=os.getenv('STAC_API_URL', None), **kwargs):
        """ Initialize a Search object with parameters """
        if url is None:
            raise SatSearchError("URL not provided, pass into Search or define STAC_API_URL environment variable")
        self.url = url.rstrip("/") + "/"
        self.kwargs = kwargs

    @classmethod
    def search(cls, headers=None, **kwargs):
        if 'query' in kwargs and isinstance(kwargs['query'], list):
            queries = {}
            for q in kwargs['query']:
                for s in Search.search_op_list:
                    parts = q.split(s)
                    if len(parts) == 2:
                        queries = dict_merge(queries, {parts[0]: {Search.search_op_to_stac_op[s]: parts[1]}})
                        break
            kwargs['query'] = queries
        directions = {'-': 'desc', '+': 'asc'}
        if 'sortby' in kwargs and isinstance(kwargs['sortby'], list):
            sorts = []
            for a in kwargs['sortby']:
                if a[0] not in directions:
                    a = '+' + a
                sorts.append({
                    'field': a[1:],
                    'direction': directions[a[0]]
                })
            kwargs['sortby'] = sorts
        return Search(**kwargs)

    def found(self, headers=None):
        """ Small query to determine total number of hits """
        kwargs = {
            'limit': 0
        }
        kwargs.update(self.kwargs)
        url = urljoin(self.url, 'search')
        results = self.query(url=url, headers=headers, **kwargs)
        # TODO - check for status_code
        logger.debug(f"Found: {json.dumps(results)}")
        found = 0
        if 'context' in results:
            found = results['context']['matched']
        elif 'numberMatched' in results:
            found = results['numberMatched']
        return found

    def query(self, headers=None, **kwargs):
        """ Get request """
        url = urljoin(self.url, 'search')
        logger.debug('Query URL: %s, Body: %s' % (url, json.dumps(kwargs)))
        response = requests.post(url, data=json.dumps(kwargs), headers=headers)
        logger.debug(f"Response: {response.text}")
        # API error
        if response.status_code != 200:
            raise SatSearchError(response.text)
        return response.json()

    def collection(self, cid, headers=None):
        """ Get a Collection record """
        url = urljoin(self.url, 'collections/%s' % cid)
        return Collection(self.query(url=url, headers=headers))

    def items(self, limit=10000, page_limit=500, headers=None):
        """ Return all of the Items and Collections for this search """
        found = self.found(headers=headers)
        if found > limit:
            logger.warning('There are more items found (%s) than the limit (%s) provided.' % (found, limit))

        nextlink = {
            'href': urljoin(self.url, 'search'),
            'headers': headers,
            'body': self.kwargs,
            'merge': False
        }

        maxitems = min(found, limit)
        items = []
        while nextlink: #and len(items) < maxitems:
            _headers = nextlink.get('headers', {})
            _body = nextlink.get('body', {})
            _body.update({'limit': page_limit})
            if nextlink.get('merge', False):
                _headers.update(headers)
                _body.update(self.kwargs)
            if nextlink.get('method', 'GET'):
                resp = self.query(url=nextlink['href'])
            else:
                resp = self.query(url=nextlink['href'], headers=_headers, **_body)
            items += [Item(i) for i in resp['features']]
            links = [l for l in resp['links'] if l['rel'] == 'next']
            nextlink = links[0] if len(links) == 1 else None

        # retrieve collections
        collections = []
        try:
            for c in set([item._data['collection'] for item in items if 'collection' in item._data]):
                collections.append(self.collection(c, headers=headers))
                #del collections[c]['links']
        except:
            pass

        return ItemCollection(items, collections=collections)

@@ -2374,24 +2374,34 @@
  query(self,
+ url=None,
  headers=Non
@@ -2447,32 +2447,39 @@
 %22%22%0A        url =
+ url or
  urljoin(self.ur
@@ -2609,31 +2609,19 @@
 rl, 
-data=json.dumps(
+json=
 kwargs
-)
 , he
@@ -3184,16 +3184,19 @@
 found = 
+0 #
 self.fou
@@ -3543,16 +3543,23 @@
 items = 
+limit #
 min(foun
@@ -3613,11 +3613,9 @@
 link
-: #
+ 
 and 
@@ -3637,16 +3637,172 @@
 xitems:%0A
+            if nextlink.get('method', 'GET'):%0A                resp = self.query(url=nextlink%5B'href'%5D, headers=headers, **self.kwargs)%0A            else:%0A    
         
@@ -3852,24 +3852,28 @@
             
+    
 _body = next
@@ -3897,32 +3897,36 @@
 %7B%7D)%0A            
+   
+ 
 _body.update(%7B'l
@@ -3945,16 +3945,20 @@
 limit%7D)%0A
+    
         
@@ -4002,32 +4002,36 @@
                 
+    
 _headers.update(
@@ -4047,32 +4047,36 @@
                 
+ 
+   
 _body.update(sel
@@ -4089,128 +4089,8 @@
 gs)%0A
-            if nextlink.get('method', 'GET'):%0A                resp = self.query(url=nextlink%5B'href'%5D)%0A            else:%0A
     
@@ -4678,16 +4678,62 @@
   pass%0A%0A
+        logger.debug(f%22Found: %7Blen(items)%7D%22)%0A%0A
         

Fix page number and url bugs