import json
import httpx
import logging
import operator
from json import JSONDecodeError

from app.configs import settings
from app.utils.extra import is_booking_allowed

logger = logging.getLogger(__name__)


FILTER_OPERATORS = {
    "==": operator.eq,
    "!=": operator.ne,
    ">": operator.gt,
    "<": operator.lt,
    ">=": operator.ge,
    "<=": operator.le,
}


def _normalize_comparable_value(value):
    if isinstance(value, str):
        stripped_value = value.strip()
        try:
            return float(stripped_value)
        except ValueError:
            return stripped_value.lower()
    return value


def _get_room_field(room, field):
    return room.get(getattr(field, "value", field))


def _matches_condition(room, condition):
    room_value = _get_room_field(room, condition.field)
    if room_value is None:
        return False

    operator_key = getattr(condition.operator, "value", condition.operator)
    comparator = FILTER_OPERATORS.get(operator_key)
    if comparator is None:
        return True

    normalized_room_value = _normalize_comparable_value(room_value)
    normalized_condition_value = _normalize_comparable_value(condition.value)

    try:
        return comparator(normalized_room_value, normalized_condition_value)
    except TypeError:
        return str(room_value).strip().lower() == str(condition.value).strip().lower()

async def search_room(adultCount,childCount,check_in,check_out,filters,room_count,hotel_ids,hotel_id,sub_hotel_id,room_type_ids = None):

    is_booking_allowed_result = await is_booking_allowed(check_in, check_out, hotel_id, sub_hotel_id, hotel_ids)
    if not is_booking_allowed_result['status']:
        yield "#qweqesds_end"
        yield "\n\n" + is_booking_allowed_result['message']
        return

    response = await get_rooms(adultCount,childCount,check_in,check_out,hotel_id,sub_hotel_id)
    
    if response.status_code != 200:
        yield "#qweqesds_end"
        if not room_type_ids:
            yield "\n\nSomething went wrong"
        return

    # Process the response content
    rooms_list=[]
    response_text = response.content.decode('utf-8-sig').strip()
    try:
        rooms_data = json.loads(response_text)
    except JSONDecodeError:
        logger.error(
            "Invalid room search response: status=%s body=%r",
            response.status_code,
            response_text[:500],
        )
        yield "#qweqesds_end"
        if not room_type_ids:
            yield "\n\nI'm sorry, live room availability is temporarily unavailable. Please try again in a moment."
        return

    if not isinstance(rooms_data, dict) or "data" not in rooms_data:
        logger.error("Unexpected room search payload: %r", rooms_data)
        yield "#qweqesds_end"
        if not room_type_ids:
            yield "\n\nI'm sorry, live room availability is temporarily unavailable. Please try again in a moment."
        return

    room_move = rooms_data['data']['room_move']

    response_status = rooms_data.get('status', '')
    error_msg = rooms_data['data'].get('errorMsg') or rooms_data.get('message', '')

    # rooms_avail = rooms_data['data'].get('rooms_avail', [])

    # Check if any room exists
    # no_rooms = all(len(item.get('available_rooms', [])) == 0 for item in rooms_avail)

    # Equivalent Angular condition
    if (not room_move) and response_status in ['error', 'failure'] and error_msg != "":
        yield "#qweqesds_end"
        yield f"\n\n{error_msg}"
        return

    selected_polcy = rooms_data['data']['selectedHotelPolicy']
    for items in rooms_data['data']['rooms_avail']:
        for item in items['available_rooms']:
            ratePlan = []
            dates = []
            price = None
            nightlySum = ""
            rates = []
            nightlyDiscount = []
            total_discount = 0
            ratePlanName = []
            for key,val in item["rates"]['2'].items():
                price = sum(float(p) for p in val)
                for text_date in val:
                    ratePlan.append(key)
                    rates.append(text_date)

            for key,val in item["dates"]['2'].items():
                for text_date in val:
                    dates.append(text_date)
                
            
            for i in range(dates.__len__()):
                nightlyDiscount.extend(['0.00'])

            for key,val in item["nightlySum"]['2'].items():
                nightlySum = val

            for key,val in item["totalDiscount"]['2'].items():
                total_discount = val

            for key,val in item["rateNameDesc"]['2'].items():
                for i in range(dates.__len__()):
                    ratePlanName.append(val['ratePlanName'])

            if item['sub_hotel_id'] not in hotel_ids:
                continue
        
            if room_type_ids and item['type_id'] not in room_type_ids:
                continue
            rooms_list.append({
                    "roomName": item['room_display_name'],
                    "roomImage": item['image'],
                    "bundleRoom": item["is_bundle"],
                    "occupancy":item["max_occupancy"],
                    "bed": item["bed_code_name"],
                    "room_type_code": item["product_id"],
                    "room_type_id": item["type_id"],
                    "rate": "{:.2f}".format(price),
                    "room_details_to_add_to_cart": {
                        "adults": str(adultCount),
                        "child": str(childCount),
                        "addCharges": item['surcharge'],
                        "cart_type":"reservation",
                        "mupog":"",
                        "dates": dates,
                        "nightlySum": nightlySum,
                        "nightlydiscount": nightlyDiscount,
                        "productId": item['product_id'],
                        "product_discount": total_discount,
                        "product_name": item['product_name'],
                        "quantity": 1,
                        "ratePlan": ratePlanName,
                        "rates": rates,
                        "roomMove": room_move,
                        "roomTypeName": item['room_type_name'],
                        "room_display_name": item['room_display_name'],
                        "room_stay_from": check_in,
                        "room_stay_to": check_out,
                        "selectedPolicyId": selected_polcy,
                        "typeId": item['type_id'],
                        "uniqueIndex": item['uniqueIndex'] }
                    })


    # Sort the rooms_list if required
    if filters and filters.sort:
        try:
            sort_field = getattr(filters.sort.field, "value", filters.sort.field)
            sort_direction = getattr(filters.sort.direction, "value", filters.sort.direction)
            rooms_list = sorted(
                rooms_list,
                key=lambda room: _normalize_comparable_value(room.get(sort_field)),
                reverse=(sort_direction == "descending"),
            )
        except (KeyError, ValueError, TypeError):
            pass

    if filters and filters.conditions:
        for condition in filters.conditions:
            rooms_list = [room for room in rooms_list if _matches_condition(room, condition)]

    if room_count != -1:
        rooms_list = rooms_list[:room_count]

    # Yield rooms one by one
    if rooms_list:
        # for room in rooms_list:
        #     yield json.dumps({'carousel': [room]})
        #     time.sleep(0.2)
        yield json.dumps({'carousel': rooms_list})
        yield "#qweqesds_end"
        count = len(rooms_list)
        yield f"\n\n{count} room{'s' if count != 1 else ''} {'is' if count == 1 else 'are'} available for the selected dates, occupancy and filters."
    else:
        yield "#qweqesds_end"
        if not room_type_ids:
            yield "\n\nIt looks like no rooms are available for the selected dates, occupancy and filters. Would you like to try different dates, occupancy and filters?"
        
async def get_rooms(adultCount,childCount,check_in,check_out,hotel_id,sub_hotel_id):
    async with httpx.AsyncClient(timeout=60.0) as client:
        response = await client.post(
            settings.LS_API_URL + "frontend/Searchresults/get_search_results_sj",
            json={
                "adults" : str(adultCount),
                "child" : str(childCount),
                "promo": "",
                "room_stay_from": check_in,
                "room_stay_to": check_out,
                "sub_hotel_id": sub_hotel_id,
                "type": "room"
            },
            headers={
                "hotel_id": hotel_id,
                "sub_hotel_id": sub_hotel_id,
            })
    return response

async def get_available_dates(hotel_id, sub_hotel_id):

    async with httpx.AsyncClient(timeout=60.0) as client:
        response = await client.get(
            settings.LS_API_URL + "frontend/Home/get_dynamic_search_dates/0",
            headers={
                "hotel_id": hotel_id,
                "sub_hotel_id": sub_hotel_id,
            })
        data = response.json()
        checkin = data['room_stay_from']['month']+"-"+data['room_stay_from']['day']+"-"+data['room_stay_from']['year']
        checkout = data['room_stay_to']['month']+"-"+data['room_stay_to']['day']+"-"+data['room_stay_to']['year'] 
        return checkin,checkout
