from datetime import datetime
import sys
import os


from utils.poi_analyzer import POIAnalyzer
from utils.time_utils import *


poi_analyzer = POIAnalyzer()

time_utils = TimeUtils()

def day_count(plan): # Return the number of days in the itinerary
    return len(plan["dayInfos"])


def allactivities(plan): # Return all activities
    activity_list = []
    for day_info in plan["dayInfos"]:
        for schedule in day_info["scheduleDetail"]:
            activity_list.extend(schedule["detailList"])  # type, id ,name
    return activity_list


def allactivities_count(plan): # Return the number of all activities
    count = 0
    for day_info in plan["dayInfos"]:
        for schedule in day_info["scheduleDetail"]:
            count += len(schedule["detailList"])
    return count


def dayactivities(plan, day): # Return the activities of a specific day
    activity_list = []
    for schedule in plan["dayInfos"][day - 1]["scheduleDetail"]:
        activity_list.extend(schedule["detailList"])  # type, id ,name
    return activity_list


def activity_transports(activity, poi_dict): # Return the transportation information of an activity     
    poi_analyzer.load_pool_from_dict(poi_dict)
    id = activity["id"]
    for transport in poi_analyzer.transportation_info:
        if transport["planid"] == id:
            return transport
    return {}

def activity_type(activity): # Return the type of an activity
    return activity.get("type", "")

def activity_name(activity): # Return the name of an activity
    return activity.get("name", "")

def start_city(plan,poi_dict): # Return the departure city
    return poi_dict['start_city']

def target_city(plan,poi_dict): # Return the destination city
    return poi_dict['target_city']

def activity_position_coordinate(activity,poi_dict): # Return the coordinate of an activity
    lat, lon = 0, 0
    if activity["type"] == "transportation":
        transport = activity_transports(activity, poi_dict)
        try:
            lat, lon = transport['to']['lat'], transport['to']['lng']
        except:
            lat, lon = 0, 0
    elif activity["type"] == "hotel":
        hotel_info = poi_analyzer.get_hotel_info(activity['id'], poi_dict['locale'])
        lat, lon = hotel_info['lat'], hotel_info['lon']
    elif activity["type"] == "poi":
        poi_api_info, min_spend_time, poi_info= poi_analyzer.read_poi_api_info(activity.get("id"),poi_dict['locale'])
        lat, lon = poi_api_info.get("lat", 0), poi_api_info.get("lon", 0)
    return lon, lat


def activity_position_city(activity,poi_dict): # Return the city of an activity
    if activity["type"] == "transportation":
        return intercity_transport_origin(activity,poi_dict)
    elif activity["type"] == "hotel":
        hotel_info = poi_analyzer.get_hotel_info(activity['id'], poi_dict['locale'])
        return hotel_info['cityName']
    elif activity["type"] == "poi":
        poi_api_info, min_spend_time, poi_info= poi_analyzer.read_poi_api_info(activity.get("id"),poi_dict['locale'])
        return poi_api_info.get("districtName", "")


def activity_open_time(activity,poi_dict): # Return the opening time of an activity
    start_time = '00:00'
    if activity["type"] == "transportation":
        transport = activity_transports(activity, poi_dict)
        start_time = transport['depature_time']
        if len(start_time) > 6:
            start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M").strftime("%H:%M")
    elif activity["type"] == "hotel":
        pass
    elif activity["type"] == "poi":
        poi_api_info, min_spend_time, poi_info= poi_analyzer.read_poi_api_info(activity.get("id"),poi_dict['locale'])
        open_time = poi_analyzer.parse_poi_time_window(poi_info)
        open_start_time, open_end_time = (open_time.split('-')[0].strip(),open_time.split('-')[1].strip()) 
        start_time = open_start_time
    return start_time

def activity_close_time(activity,poi_dict): # Return the closing time of an activity
    end_time = '00:00'
    if activity["type"] == "transportation":
        transport = activity_transports(activity, poi_dict)
        end_time = transport['flight_time']
        end_time = datetime.strptime(end_time, "%Y-%m-%d %H:%M").strftime("%H:%M")
    elif activity["type"] == "hotel":
        pass
    elif activity["type"] == "poi":
        poi_api_info, min_spend_time, poi_info= poi_analyzer.read_poi_api_info(activity.get("id"),poi_dict['locale'])
        open_time = poi_analyzer.parse_poi_time_window(poi_info)
        open_start_time, open_end_time = (open_time.split('-')[0].strip(),open_time.split('-')[1].strip()) 
        end_time = open_end_time
    return end_time

def activity_time(activity,poi_dict): # Return the duration of an activity

    start_time = activity_open_time(activity,poi_dict)
    end_time = activity_close_time(activity,poi_dict)

    if start_time and end_time:
        st_h, st_m = map(int, start_time.split(":"))
        ed_h, ed_m = map(int, end_time.split(":"))
        return (ed_m - st_m) + (ed_h - st_h) * 60
    else:
        return -1

def poi_recommend_time(activity,poi_dict): # Return the recommended time for an activity
    poi_api_info, min_spend_time, poi_info= poi_analyzer.read_poi_api_info(activity.get("id"),poi_dict['locale'])
    min_hours, max_hours = poi_analyzer.calculate_poi_hours(poi_api_info)  # The minimum and maximum time for playing
    recommend_time = min_hours
    # recommend_time = (min_hours + max_hours) / 2  # The recommended time for playing is the average of the minimum and maximum time
    return recommend_time


def poi_distance(activity1,activity2,poi_dict): # Return the distance between two activities
    lat1, lon1 = activity_position_coordinate(activity1,poi_dict)
    lat2, lon2 = activity_position_coordinate(activity2,poi_dict)
    if lat1 and lon1 and lat2 and lon2:
        distance = poi_analyzer.calculate_distance(lat1, lon1, lat2, lon2)
    else:
        distance = 0
    return distance


def innercity_transport_distance(activity1,activity2,poi_dict): # Return the distance of inner city transportation
    """
    Calculate the distance of inner city transportation
    Args:
        transports: The list of transportation information
    Returns:
        float: The total distance of the selected type of transportation, if mode is None, then return the total distance of all types
    """
    distance = 0
    distance = poi_distance(activity1,activity2,poi_dict)
    return distance


def innercity_transport_time(activity1,activity2,poi_dict): # Return the time of inner city transportation
    distance = poi_distance(activity1,activity2,poi_dict)
    time = poi_analyzer.calculate_transportation_time(distance)
    return time


def attraction_type(activity, poi_dict): # Return the type of attraction
    poi_api_info, min_spend_time, poi_info= poi_analyzer.read_poi_api_info(activity.get("id"),poi_dict['locale'])
    tag_list = poi_api_info.get("tag", [])
    return ','.join(tag_list)


def innercity_transport_type(activity1,activity2,poi_dict): # Return the type of inner city transportation
    distance = poi_distance(activity1,activity2,poi_dict)
    # Take walking
    if distance < 2:
        return "walk"
    # Take metro
    elif distance < 20:
        return "metro"
    # Take taxi
    else:
        return "taxi"


def accommodation_type(activity, poi_dict): # Return the type of accommodation
    hotel_info = poi_analyzer.get_hotel_info(activity['id'], poi_dict['locale'])
    hotel_tag = hotel_info['tagList']
    return ','.join(hotel_tag)


def intercity_transport_origin(activity,poi_dict): # Return the origin of inter city transportation
    trans_activity = activity_transports(activity, poi_dict)
    return trans_activity['key'].split('->')[0]



def intercity_transport_destination(activity,poi_dict): # Return the destination of inter city transportation 
    trans_activity = activity_transports(activity, poi_dict)
    return trans_activity['key'].split('->')[1]


def intercity_transport_type(activity,poi_dict): # Return the type of inter city transportation
    transport = activity_transports(activity, poi_dict)
    card_type = transport['cardType']
    type_list = []
    for card_type in card_type:
        if card_type == 'F':
            type_list.append('flight')
        elif card_type == 'T':
            type_list.append('train')
        else:
            type_list.append(card_type)
    return '->'.join(type_list)


def activity_price(activity,poi_dict): # Return the price of an activity
    if activity["type"] == "transportation":
        transport = activity_transports(activity, poi_dict)
        return transport['price']
    elif activity["type"] == "hotel":
        hotel_info = poi_analyzer.get_hotel_info(activity['id'], poi_dict['locale'])
        price = hotel_info['price']
        return price
    elif activity["type"] == "poi":
        poi_api_info, min_spend_time, poi_info= poi_analyzer.read_poi_api_info(activity.get("id"),poi_dict['locale'])
        return poi_api_info.get("price", 0)
    else:
        return 0

def day_period_activities(plan,day): # Return the activities of a specific day
    period_activities = {}
    for schedule in plan["dayInfos"][day - 1]["scheduleDetail"]:
        period_activities[schedule["period"]] = schedule["detailList"]
    return period_activities


def time_add(time1, time2): # Return the time of time addition
    return time_utils.time_operation_add(time1, time2)

def time_compare(time1, time2): # Return the time of time comparison
    return time_utils.compare_times_later(time1, time2)


def is_chronological_complex(plan,poi_dict):
    result=True
    all_activity_times = []  # The global activity time list, used to check across days
    days = day_count(plan)
    for day in range(days):
        pre_start_time,pre_end_time = "08:00","08:00"  # Initialize to valid time
        pre_activity = ""
        activity_times = []  # Used to check time overlap
        for activity in dayactivities(plan,day+1):
            if activity_type(activity) == 'transportation':
                if pre_activity:
                    min_trans_time = innercity_transport_time(pre_activity, activity, poi_dict)
                    cur_start_time = time_add(pre_end_time, min_trans_time)
                    cur_end_time = time_add(pre_end_time, min_trans_time)
                else:
                    cur_start_time, cur_end_time = activity_open_time(activity, poi_dict), activity_close_time(activity, poi_dict)
                # Check if the start time is later than the end time
                if time_compare(cur_start_time, cur_end_time) > 0:
                    result=False
                    break
                # Check if the start time is later than the opening time of the activity
                if time_compare(cur_start_time, activity_open_time(activity,poi_dict)) > 0:
                    result=False
                    break
                if cur_start_time and cur_end_time:
                    activity_times.append({
                        'type': 'transportation',
                        'id': activity['id'],
                        'start': cur_start_time,
                        'end': cur_end_time,
                        "day": day+1
                    })
                pre_start_time, pre_end_time = cur_start_time, cur_end_time
                pre_activity = activity

            elif activity_type(activity) == 'hotel':
                if pre_activity:
                    min_trans_time = innercity_transport_time(pre_activity, activity, poi_dict)
                    cur_start_time = time_add(pre_end_time, min_trans_time)
                    cur_end_time = time_add(pre_end_time, min_trans_time)
                else:
                    cur_start_time = time_add(pre_end_time, 1)
                    cur_end_time = time_add(pre_end_time,2)
                activity_times.append({
                            'type': 'hotel',
                            'id': activity['id'],
                            'start': cur_start_time,
                            'end': cur_end_time,
                            'day': day+1
                        })
                pre_start_time, pre_end_time = cur_start_time, cur_end_time
                pre_activity = activity

            
            elif activity_type(activity) == 'poi':
                if pre_activity:
                    min_trans_time = innercity_transport_time(pre_activity, activity, poi_dict)
                    cur_start_time = time_add(pre_end_time, min_trans_time) if time_compare(pre_end_time, activity_open_time(activity,poi_dict)) > 0 else activity_open_time(activity,poi_dict)
                    cur_end_time = time_add(cur_start_time, poi_recommend_time(activity,poi_dict))  # The arrival time + the time for playing
                else:
                    cur_start_time = activity_open_time(activity,poi_dict) if activity_open_time(activity,poi_dict)!="00:00" else "09:00"
                    cur_end_time = time_add(cur_start_time,poi_recommend_time(activity,poi_dict))
                # Check if the start time is later than the closing time of the activity
                if time_compare(cur_start_time, activity_close_time(activity,poi_dict)) > 0:
                    result=False
                    break
                # Adjust the end time to not exceed the closing time of the activity
                cur_end_time = cur_end_time if time_compare(cur_end_time, activity_close_time(activity,poi_dict)) < 0 else activity_close_time(activity,poi_dict)
                if cur_start_time and cur_end_time:
                    activity_times.append({
                        'type': 'poi',
                        'id': activity['id'],
                        'start': cur_start_time,
                        'end': cur_end_time,
                        "day": day+1
                    })
                pre_start_time, pre_end_time = cur_start_time, cur_end_time
                pre_activity = activity
        all_activity_times.extend(activity_times)
        # Check if the activity time overlaps
        for i in range(len(activity_times)):
            for j in range(i + 1, len(activity_times)):
                time1 = activity_times[i]
                time2 = activity_times[j]
                
                # Check if the time overlaps
                # If the start time of time1 is earlier than the end time of time2, and the start time of time2 is earlier than the end time of time1, then there is overlap
                if (time_compare(time1['start'], time2['end']) < 0 and 
                    time_compare(time2['start'], time1['end']) < 0):
                    result=False
                    break
    return result


def is_consistent_city(plan,poi_dict):
    result=True
    current_location = poi_dict.get("start_city", "")  # The departure city
    trans_activity = ''
    for activity in allactivities(plan):
        if activity_type(activity) == 'transportation':
            if current_location != intercity_transport_origin(activity,poi_dict):
                result=False
                break
            current_location = intercity_transport_destination(activity,poi_dict)  # Update the current location
            trans_activity = activity

        elif activity_type(activity) == 'poi':
            location = activity_position_city(activity,poi_dict)
            distance = poi_distance(trans_activity,activity,poi_dict)
            if trans_activity:
                if location != current_location and distance > 3000:
                    result=False
                    break
        
        elif activity_type(activity) == 'hotel':
            distance = poi_distance(trans_activity,activity,poi_dict)
            if current_location != activity_position_city(activity,poi_dict) and distance > 3000:
                result=False
                break
            trans_activity = activity
    return result


def is_open_time_complex(plan,poi_dict):
    result=True
    days = day_count(plan)
    for day in range(days):     
        pre_start_time,pre_end_time = "08:00","08:00"  # Initialize to valid time
        pre_activity = ""
        for activity in dayactivities(plan, day+1):
            if activity_type(activity) == 'transportation':
                cur_start_time, cur_end_time = activity_open_time(activity, poi_dict), activity_close_time(activity, poi_dict)
                pre_start_time, pre_end_time = cur_start_time, cur_end_time
                pre_activity = activity

            elif activity_type(activity) == 'hotel':
                if pre_activity:
                    min_trans_time = innercity_transport_time(pre_activity, activity, poi_dict)
                    cur_start_time = time_add(pre_end_time, min_trans_time)
                    cur_end_time = time_add(pre_end_time, min_trans_time)
                else:
                    cur_start_time = time_add(pre_end_time, 1)
                    cur_end_time = time_add(pre_end_time,2)
                pre_start_time, pre_end_time = cur_start_time, cur_end_time
                pre_activity = activity

            
            elif activity_type(activity) == 'poi':
                if pre_activity:
                    min_trans_time = innercity_transport_time(pre_activity, activity, poi_dict)
                    cur_start_time = time_add(pre_end_time, min_trans_time) 
                    # Check if the arrival time is later than the closing time of the attraction
                    if time_compare(cur_start_time, activity_close_time(activity,poi_dict)) > 0:
                        result=False
                        break
                    cur_start_time = cur_start_time if time_compare(cur_start_time, activity_open_time(activity,poi_dict)) > 0 else activity_open_time(activity,poi_dict)
                    cur_end_time = time_add(cur_start_time, poi_recommend_time(activity,poi_dict))  # The arrival time + the time for playing
                else:
                    cur_start_time = activity_open_time(activity,poi_dict) if activity_open_time(activity,poi_dict)!="00:00" else "09:00"
                    cur_end_time = time_add(cur_start_time,poi_recommend_time(activity,poi_dict))
                
                cur_end_time = cur_end_time if time_compare(cur_end_time, activity_close_time(activity,poi_dict)) < 0 else activity_close_time(activity,poi_dict)
                pre_start_time, pre_end_time = cur_start_time, cur_end_time
                pre_activity = activity
        if not result:
            break
    return result


def is_blockout_time(plan,poi_dict):
    result=True
    days = day_count(plan)
    for day in range(days):
        cross_flight_day = 0
        end_time_hour = 0
        period_activities = day_period_activities(plan,day+1)
        for period, activities in period_activities.items():
            for activity in activities:
                if activity_type(activity) == 'transportation':
                    transport = activity_transports(activity, poi_dict)
                    start_time = time_utils.parse_time(transport['depature_time'])
                    end_time = time_utils.parse_time(transport['flight_time'])
                    cross_transport = 1 if start_time.day != end_time.day else 0
                    end_time_hour = end_time.hour
                    cross_flight_day = day + (end_time.day - start_time.day)

                if cross_transport==0:  # The same day
                    if activity_type(activity) in ["poi", "hotel"]:
                        if end_time_hour>12 and period in ["Morning", "上午"]:
                            result=False
                            break
                        if end_time_hour>17 and period in ["Morning", "Afternoon", "上午", "下午"]:
                            result=False
                            break
                    if activity_type(activity) in ["poi"] and end_time_hour > 19 and period in ["Evening", "晚上"]:
                        result=False
                        break
                else:
                    if day < cross_flight_day:
                        if activity_type(activity) in ["poi", "hotel"]:
                            result=False
                            break
                    elif day >= cross_flight_day:
                        if activity_type(activity) in ["poi", "hotel"]:
                            if end_time_hour>12 and period in ["Morning", "上午"]:
                                result=False
                                break
                            if end_time_hour>17 and period in ["Morning", "Afternoon", "上午", "下午"]:
                                result=False
                                break
                        if activity_type(activity) in ["poi"] and end_time_hour > 19 and period in ["Evening", "晚上"]:
                            result=False
                            break
                        cross_flight_day = 0
                        end_time_hour = 0
        if not result:
            break
    return result


def is_early_transport(plan,poi_dict):
    result=True
    for day in range(day_count(plan)):
        pre_activity_num = 0
        for activity in dayactivities(plan,day+1):
            if activity_type(activity) == 'transportation':
                start_time = activity_open_time(activity,poi_dict)
                if time_compare("10:00",start_time) > 0 and pre_activity_num > 0:
                    result=False
                    break
            else:
                pre_activity_num +=1
    return result


def is_consistent_transportation(plan,poi_dict):
    transport_sequence = []
    for activity in allactivities(plan):
        if activity_type(activity) == 'transportation':
            transport_sequence.append(activity)
    result=True
    for i in range(len(transport_sequence)-1):
        if intercity_transport_destination(transport_sequence[i],poi_dict) != intercity_transport_origin(transport_sequence[i+1],poi_dict):
            result=False
            break
    return result


def is_chronological(plan,poi_dict):  # No consideration for inner city transportation time
    result=True
    period_mapping = {
            "Morning": 1,
            "Afternoon": 2,
            "Evening":3
        }
    for day in range(day_count(plan)):
        period_list = []
        for day_info in plan["dayInfos"][day]["scheduleDetail"]:
            period_list.append(period_mapping[day_info["period"]])
        for i in range(len(period_list)-1):
            if period_list[i] > period_list[i+1]:
                result=False
                break
    
    for day in range(day_count(plan)):
        activity_times = []
        for activity in dayactivities(plan,day+1):
            activity_times.append({
                'start': activity['start_time'],
                'end': activity['end_time']
            })
        # Check if the activity time overlaps
        for i in range(len(activity_times)):
            for j in range(i + 1, len(activity_times)):
                time1 = activity_times[i]
                time2 = activity_times[j]

                # Check if the time overlaps
                # If the start time of time1 is earlier than the end time of time2, and the start time of time2 is earlier than the end time of time1, then there is overlap
                if (time_compare(time1['start'], time2['end']) < 0 and 
                    time_compare(time2['start'], time1['end']) < 0):
                    result=False
                    break
    return result



def is_open_time(plan,poi_dict):
    result=True
    for activity in allactivities(plan):
        if activity_type(activity) == 'poi':
            poi_api_info, min_spend_time, poi_info= poi_analyzer.read_poi_api_info(activity.get("id"),poi_dict['locale'])
            open_time = poi_analyzer.parse_poi_time_window(poi_info)
            open_start_time, open_end_time = (open_time.split('-')[0].strip(),open_time.split('-')[1].strip()) 
            if TimeUtils.compare_times_later(open_start_time,open_end_time)>0:
                result=False
                break

            if TimeUtils.compare_times_later(activity['end_time'],open_end_time)>0:
                result=False
                break
            if TimeUtils.compare_times_later(open_start_time,activity['start_time'])>0:
                result=False
                break
            if TimeUtils.compare_times_later(open_start_time,activity['end_time'])>0:
                result=False
                break
            if TimeUtils.compare_times_later(activity['start_time'],open_end_time,)>0:
                result=False
                break

    return result

func_dict = {
    "day_count": day_count,
    "start_city": start_city,
    "target_city": target_city,
    "allactivities": allactivities,
    "allactivities_count": allactivities_count,
    "activity_position_coordinate": activity_position_coordinate,
    "activity_position_city": activity_position_city,
    "activity_type": activity_type,
    "activity_name": activity_name,
    "activity_transports": activity_transports,
    "activity_price": activity_price,
    "activity_time": activity_time,
    "poi_recommend_time": poi_recommend_time,
    "poi_distance": poi_distance,
    "attraction_type": attraction_type,
    "accommodation_type": accommodation_type,
    "innercity_transport_type": innercity_transport_type,
    "innercity_transport_distance": innercity_transport_distance,
    "intercity_transport_type": intercity_transport_type,
    "dayactivities": dayactivities,
    "activity_open_time": activity_open_time,
    "activity_close_time": activity_close_time,
    "intercity_transport_origin": intercity_transport_origin,
    "intercity_transport_destination": intercity_transport_destination, 
    "innercity_transport_time": innercity_transport_time,
    "is_chronological": is_chronological,
    "is_consistent_city": is_consistent_city,
    "is_open_time": is_open_time,
    "is_blockout_time": is_blockout_time,
    "is_early_transport": is_early_transport,
    "is_consistent_transportation": is_consistent_transportation,
}
# 32 functions


if __name__ == "__main__":
    # Fix: fix the constraint string content, fix the way to get the variable after exec, fix the way to get the result
    constraint = (
        "food_cost = 0\n"
        "total_cost = 0\n"
        "for activity in allactivities(plan):\n"
        "    total_cost += activity_cost(activity)\n"
        "    total_cost += innercity_transport_cost(activity_transports(activity))\n"
        "    if activity_type(activity) in ['breakfast', 'lunch', 'dinner']:\n"
        "        food_cost += activity_cost(activity)\n"
        "food_cost_ratio = food_cost / total_cost if total_cost > 0 else -1\n"
        "result = food_cost_ratio"
    )

    plan = {"dayInfos":[{"day":1,"scheduleDetail":[{"period":"Afternoon","detailList":[{"id":"759690032385627571","type":"transportation","name":"759690032385627571","start_time":"2025-06-24 13:15","end_time":"2025-06-24 14:25","start":"Singapore","end":"Cameron Highlands","price":684,"cost":684,"tickets":1,"transports":[]},{"id":"18105374","name":"Sunway Lost World of Tambun","type":"poi","price":126,"cost":126,"start_time":"15:16","end_time":"19:16","transports":[{"mode":"metro","distance":30.26,"time":0.86,"price":37.83,"cost":37.83,"tickets":1,"start_time":"2025-06-24 14:25","end_time":"15:16","start_location":"(4.4721201, 101.3801441)","end_location":"(4.625942, 101.1549418)"}],"tickets":1,"room_type":2,"rooms":1}]},{"period":"Evening","detailList":[{"id":"30562902","name":"Panglima Lane","type":"poi","price":0,"cost":0,"start_time":"19:31","end_time":"20:31","transports":[{"mode":"metro","distance":9.18,"time":0.26,"price":11.47,"cost":11.47,"tickets":1,"start_time":"19:16","end_time":"19:31","start_location":"(4.625942, 101.1549418)","end_location":"(4.5964216, 101.0776378)"}],"tickets":1},{"id":"101236381","name":"Hotel Brynton at Cameron Centrum Mall","type":"hotel","price":200,"cost":200,"start_time":"21:33","end_time":"24:00","transports":[{"mode":"metro","distance":36.35,"time":1.04,"price":45.43,"cost":45.43,"tickets":1,"start_time":"20:31","end_time":"21:33","start_location":"(4.5964216, 101.0776378)","end_location":"(4.4947, 101.389246)"}]}]}]},{"day":2,"scheduleDetail":[{"period":"Morning","detailList":[{"id":"133880579","name":"Cameron Highland Butterfly Farm","type":"poi","price":0,"cost":0,"start_time":"08:04","end_time":"09:04","transports":[{"mode":"metro","distance":2.4,"time":0.07,"price":3.0,"cost":3.0,"tickets":1,"start_time":"08:00","end_time":"08:04","start_location":"(4.4947, 101.389246)","end_location":"(4.5072601, 101.4068827)"}],"tickets":1},{"id":"18221684","name":"Boh Tea Centre Habu","type":"poi","price":0,"cost":0,"start_time":"09:15","end_time":"12:15","transports":[{"mode":"metro","distance":6.79,"time":0.19,"price":8.49,"cost":8.49,"tickets":1,"start_time":"09:04","end_time":"09:15","start_location":"(4.5072601, 101.4068827)","end_location":"(4.4482374, 101.4226685)"}],"tickets":1,"room_type":2,"rooms":1}]},{"period":"Afternoon","detailList":[{"id":"38730380","name":"Ipoh World at Han Chin Pet Soo","type":"poi","price":0,"cost":0,"start_time":"13:26","end_time":"14:26","transports":[{"mode":"metro","distance":41.5,"time":1.19,"price":51.87,"cost":51.87,"tickets":1,"start_time":"12:15","end_time":"13:26","start_location":"(4.4482374, 101.4226685)","end_location":"(4.596307, 101.079021)"}],"tickets":1},{"id":"101236381","name":"Hotel Brynton at Cameron Centrum Mall","type":"hotel","price":200,"cost":200,"start_time":"15:28","end_time":"24:00","transports":[{"mode":"metro","distance":36.2,"time":1.03,"price":45.24,"cost":45.24,"tickets":1,"start_time":"14:26","end_time":"15:28","start_location":"(4.596307, 101.079021)","end_location":"(4.4947, 101.389246)"}]}]}]},{"day":3,"scheduleDetail":[{"period":"Morning","detailList":[{"id":"147205531","name":"Big Red Strawberry Farm","type":"poi","price":0,"cost":0,"start_time":"08:00","end_time":"09:00","transports":[{"mode":"metro","distance":0.3,"time":0.01,"price":2.5,"cost":2.5,"tickets":1,"start_time":"08:00","end_time":"08:00","start_location":"(4.4947, 101.389246)","end_location":"(4.4954885, 101.3866714)"}],"tickets":1},{"id":"58280408","name":"Agro Technology Park MARDI Cameron Highlands","type":"poi","price":0,"cost":0,"start_time":"09:05","end_time":"11:05","transports":[{"mode":"metro","distance":3.07,"time":0.09,"price":3.84,"cost":3.84,"tickets":1,"start_time":"09:00","end_time":"09:05","start_location":"(4.4954885, 101.3866714)","end_location":"(4.4679283, 101.3847448)"}],"tickets":1}]},{"period":"Evening","detailList":[{"id":"395912881123083210","type":"transportation","name":"395912881123083210","start_time":"2025-06-26 17:25","end_time":"2025-06-26 18:45","start":"Cameron Highlands","end":"Singapore","price":2026,"cost":2026,"tickets":1,"transports":[{"mode":"metro","distance":0.69,"time":0.02,"price":2.5,"cost":2.5,"tickets":1,"start_time":"11:05","end_time":"11:06","start_location":"(4.4679283, 101.3847448)","end_location":"(4.4721201, 101.3801441)"}]}]}]}]}
    constraint = ("result = (is_chronological==1)")
    # Copy func_dict to avoid polluting the global
    import copy
    local_vars = copy.deepcopy(func_dict)
    result = is_open_time(plan,{'locale':'en-US'})
    print(result)

    # try:
    #     exec(
    #         constraint,
    #         {
    #             "__builtins__": {
    #                 "set": set                
    #                 }
    #         },
    #         local_vars,
    #     )
    # except Exception as e:
    #     print(f"Error: {e}")
    # res_i = local_vars.get("result")
    # print(f"Result: {res_i}")
