from django.conf import settings
from bson import ObjectId
from datetime import datetime

def get_mongo_match(query_params, filter_fields):
    match = {}
    # Define MongoDB filter types
    filter_types = {
        'exact': lambda field, value: {field: int(value) if field.endswith('_id') and field != 'listing_id' else value},
        ### filter for ObjectId fields
        'exact_oid': lambda field, value: {field: ObjectId(value)},
        ### filter specific for boolean fields
        'exact_boolean': lambda field, value: {field: True if value.lower() == 'true' else False},
        ### filter for datetime fields -   format 2024-01-01   rest framework format 2024-01-01T00:00:00.000Z, mongodb format ISODate("2024-06-12T14:26:49.805Z"),
        'exact_datetime': lambda field, value: {field: datetime.strptime(value, '%Y-%m-%d')},
        #'exact_datetime': lambda field, value: {field: datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')},
        'icontains': lambda field, value: {field: {'$regex': value, '$options': 'i'}},
        'in': lambda field, value: {field: {'$in': [v.strip() for v in value.split(',')] if ',' in value else [value]}},
        'range': lambda field, value: {field: {'$gte': float(value.split(',')[0]), '$lte': float(value.split(',')[1])}},
        ### filter for range datetime fields -   format 2024-01-01   rest framework format 2024-01-01T00:00:00.000Z
        'range_datetime': lambda field, value: {field: {'$gte': datetime.strptime(value.split(',')[0], '%Y-%m-%d'), '$lte': datetime.strptime(value.split(',')[1], '%Y-%m-%d')}},
        #'range_datetime': lambda field, value: {field: {'$gte': datetime.strptime(value.split(',')[0], '%Y-%m-%dT%H:%M:%S.%fZ'), '$lte': datetime.strptime(value.split(',')[1], '%Y-%m-%dT%H:%M:%S.%fZ')}},
        'gte': lambda field, value: {field: {'$gte': float(value)}},
        #### filter for get datetime fields
        'gte_datetime': lambda field, value: {field: {'$gte': datetime.strptime(value, '%Y-%m-%d')}},
        'lt': lambda field, value: {field: {'$lt': float(value)}},
        ### filter for lt datetime fields
        #'lt_datetime': lambda field, value: {field: {'$lt': datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')}},
        #'lt_datetime': lambda field, value: {field: {'$lt': datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')}},
        'lt_datetime': lambda field, value: {field: {'$lt': datetime.strptime(value, '%Y-%m-%d')}},
        'lte': lambda field, value: {field: {'$lte': float(value)}},
        ### filter for lte datetime fields
        'lte_datetime': lambda field, value: {field: {'$lte': datetime.strptime(value, '%Y-%m-%d')}},
        'gt': lambda field, value: {field: {'$gt': float(value)}},
        ### filter for gt datetime fields
        #'gt_datetime': lambda field, value: {field: {'$gt': datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')}},
        'gt_datetime': lambda field, value: {field: {'$gt': datetime.strptime(value, '%Y-%m-%d')}},
        'regex': lambda field, value: {field: {'$regex': value}},
        'iregex': lambda field, value: {field: {'$regex': value, '$options': 'i'}},
        'iexact': lambda field, value: {field: {'$regex': f"^{value}$", '$options': 'i'}},
        'startswith': lambda field, value: {field: {'$regex': f"^{value}"}},
        'istartswith': lambda field, value: {field: {'$regex': f"^{value}", '$options': 'i'}},
        'endswith': lambda field, value: {field: {'$regex': f"{value}$"}},
        'iendswith': lambda field, value: {field: {'$regex': f"{value}$", '$options': 'i'}}
    }
    for key, values in query_params.items():
        if not values:
            continue
        value = values[0] if isinstance(values, list) else values
        parts = key.split('__')
        field = '__'.join(parts[:-1]) if len(parts) > 1 and parts[-1] in filter_types else key
        filter_type = parts[-1] if len(parts) > 1 and parts[-1] in filter_types else 'exact'
        if field in filter_fields and filter_type in filter_fields[field]:
            method = filter_types.get(filter_type, lambda f, v: {f: v})
            field = field.replace('__', '.')
            match.update(method(field, value))
    return match

def build_aggregation_pipeline(request, filter_fields):

    match = get_mongo_match(dict(request.query_params), filter_fields)
    pipeline = [{'$match': match}]

    
    # Handle ordering
    ordering = request.query_params.get('ordering', '-_id').strip()
    sort_order = -1 if ordering.startswith('-') else 1
    sort_field = ordering.lstrip('-')
    pipeline.append({'$sort': {sort_field: sort_order}})
    
    # Handle pagination
    ## get the page size from settings.py
    page_size_default = settings.REST_FRAMEWORK.get('PAGE_SIZE', 10)
    page_size = int(request.query_params.get('page_size', page_size_default))
    page = int(request.query_params.get('page', 1))
    skip = (page - 1) * page_size
    pipeline.extend([{'$skip': skip}, {'$limit': page_size}])
    
    # Optionally add projection to reduce fields returned
    pipeline.append({'$project': {'_id': 1}})
    
    # Pipeline Filter only with filters without limit, returning count documents
    pipeline_filter = [{'$match': match}]
    #Count the ddocuments
    pipeline_filter.append({'$count': 'total'})
    
    ### DEBUG PIPELINE
    #print(f"DEBUG Pipeline: {pipeline}")
    #print(f"DEBUG Pipeline Filter: {pipeline_filter}")
    
    return pipeline, pipeline_filter




from urllib.parse import urlencode, urlparse, urlunparse, parse_qs

def build_absolute_uri(request, new_params):
    url_parts = list(urlparse(request.build_absolute_uri()))
    query = parse_qs(url_parts[4])
    query.update(new_params)
    url_parts[4] = urlencode(query, doseq=True)
    return urlunparse(url_parts)