"""
Commonsense Evaluator - Evaluate the commonsense constraints of the itinerary
"""

from typing import Dict, Any, Tuple, List

from .base_evaluator import BaseEvaluator
from utils.time_utils import TimeUtils
from utils.entity_utils import EntityUtils
from utils.poi_analyzer import POIAnalyzer
from utils.itinerary_parser import ItineraryParser


class CommonsenseEvaluator(BaseEvaluator):
    """Commonsense Evaluator Class"""
    
    def __init__(self, poi_analyzer: POIAnalyzer = None):
        """
        Initialize Commonsense Evaluator
        
        Args:
            poi_analyzer: POI Analyzer Instance, create new instance if None
        """
        super().__init__("CommonsenseEvaluator")
        self.entity_utils = EntityUtils()
        self.poi_analyzer = poi_analyzer if poi_analyzer is not None else POIAnalyzer()
    
    def evaluate(self, data: Dict[str, Any]) -> Tuple[float, Dict[str, Any]]:
        """
        Evaluate the commonsense constraints
        
        Args:
            data: Dictionary containing the following keys:
                - all_content: Content Data
                - itinerary: Itinerary Data
                - poi_dict: POI Dictionary
                
        Returns:
            Tuple[float, Dict]: (Commonsense Score, Evaluation Details)
        """
        itinerary = data.get("itinerary", {})
        poi_dict = data.get("poi_dict", {})
        day_infos = itinerary.get("dayInfos", {})

        violations = []
        total_score = 1.0
        # 0.Information Completeness Check
        transport_validation_result = ItineraryParser._validate_transportation_completeness(day_infos, poi_dict, self.poi_analyzer)
        if transport_validation_result:
            violations.append(transport_validation_result.get("fail", {}).get("Information Completeness", {}))
            total_score -= 0.5
        hotel_validation_result = ItineraryParser._validate_hotel_completeness(day_infos, poi_dict, self.poi_analyzer)
        if hotel_validation_result:
            violations.append(hotel_validation_result.get("fail", {}).get("Information Completeness", {}))
            total_score -= 0.5

        # 1. Time Order Check
        chronological_violations = self._check_chronological_order(itinerary,poi_dict)
        if chronological_violations:
            violations.extend(chronological_violations)
            total_score -= 0.5
        
        # 2. Location Consistency Check
        location_violations = self._check_location_consistency(itinerary,poi_dict)
        if location_violations:
            violations.extend(location_violations)
            total_score -= 0.4
        
        # 3. Operating Hours Check
        hours_violations = self._check_operating_hours(itinerary, poi_dict)
        if hours_violations:
            violations.extend(hours_violations)
            total_score -= 0.3
        
        # 4. Travel Time Blockout Check
        blockout_violations = self._check_travel_blockout(itinerary)
        if blockout_violations:
            violations.extend(blockout_violations)
            total_score -= 0.2
        
        # 5. Early Transport Rule Check
        early_transport_violations = self._check_early_transport_rule(itinerary)
        if early_transport_violations:
            violations.extend(early_transport_violations)
            total_score -= 0.2
        
        # 6. Transport Continuity Check
        continuity_violations = self._check_transport_continuity(itinerary)
        if continuity_violations:
            violations.extend(continuity_violations)
            total_score -= 0.3
        
        # If there are no violations, give a reward score
        if not violations:
            total_score = 1.0

        total_score = max(total_score, 0)

        details = {
            "violations": violations,
            "total_violations": len(violations),
            "chronological_violations": [v for v in violations if "chronological" in v],
            "location_violations": [v for v in violations if "location" in v],
            "hours_violations": [v for v in violations if "hours" in v],
            "stay_violations": [v for v in violations if "stay" in v],
            "blockout_violations": [v for v in violations if "blockout" in v],
            "early_transport_violations": [v for v in violations if "early_transport" in v],
            "continuity_violations": [v for v in violations if "continuity" in v]
        }
        
        return total_score, details
    
    def _check_chronological_order(self, itinerary: Dict,poi_dict: Dict) -> List[str]:
        """Check Time Order"""
        violations = []
        pre_day = 0
        all_activity_times = []  # Global Activity Time List, used for cross-day check
        id_have = []
        for day_data in itinerary['dayInfos']:
            if not isinstance(day_data, dict):
                continue
            day_key = day_data.get("day", 0)
            # Check if the day number is in chronological order
            if pre_day > day_key:
                violations.append(f"chronological_violation: day {day_key} occurs before day {pre_day}")
            pre_day = day_key

            activities,period_list = [],[]
            scheduleDetail = day_data.get("scheduleDetail", [])
            for schedule in scheduleDetail: 
                activities.extend(schedule.get("detailList", []))
                period_list.append(schedule.get("period", ""))

            if not activities or not period_list:
                continue

            # Check if the period_list is in chronological order
            right_period_mapping = {
                "Morning":1,
                '上午':1,
                "Afternoon":2,
                "下午":2,
                "Evening":3,
                "晚上":3
            }
            period_list = [right_period_mapping.get(period, 0) for period in period_list]
            # If the previous value is greater than the latter value, it is considered a violation
            for i in range(len(period_list) - 1):
                if period_list[i] > period_list[i+1]:
                    violations.append(f"chronological_violation: period {period_list[i]} occurs after period {period_list[i+1]} on day {day_key}")

                
            # Check if the activities are in chronological order
            pre_start_time,pre_end_time = "",""
            activity_times = []  # Used to check time overlap
            pre_lat, pre_lon = 0,0
            
            for activity in activities:

                if activity['type'] == 'transportation' and activity['id'] not in id_have:
                    for transportation in self.poi_analyzer.transportation_info:
                        if activity['id'] == transportation['planid']:
                            cur_start_time, cur_end_time = transportation['depature_time'], transportation['flight_time'] 
                            if TimeUtils.compare_times_later(cur_start_time, cur_end_time) > 0:
                                violations.append(f"chronological_violation: Transportation {activity['id']} start time {cur_start_time} is after end time {cur_end_time} on day {day_key}")
                            if pre_end_time and TimeUtils.compare_times_later(pre_end_time, cur_start_time) > 0:
                                violations.append(f"chronological_violation: last activity ends at {pre_end_time} while the transportation start at {cur_start_time} on day {day_key}")
                            # Record activity time for overlap check
                            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_key
                                })
                            id_have.append(activity['id'])
                            
                            pre_start_time, pre_end_time = TimeUtils.extract_time_only(cur_start_time),TimeUtils.extract_time_only(cur_end_time)  
                            break

                elif activity['type'] == 'poi':
                    poi_api_info, min_spend_time, poi_info= self.poi_analyzer.read_poi_api_info(activity['id'], poi_dict['locale'])
                    if not poi_api_info:
                        continue
                    open_time = self.poi_analyzer.parse_poi_time_window(poi_info)
                    open_start_time, open_end_time = (open_time.split('-')[0].strip(),open_time.split('-')[1].strip())  
                    min_hours, max_hours = self.poi_analyzer.calculate_poi_hours(poi_api_info)  # Minimum and Maximum Time for Playing
                    poi_lat, poi_lon = poi_api_info.get("lat", 0), poi_api_info.get("lon", 0)
                    if pre_lat and pre_lon:
                        min_trans_time = self.poi_analyzer.calculate_transportation_time_by_lat_lon(pre_lat, pre_lon, poi_lat, poi_lon) # Hours
                        cur_start_time = TimeUtils.time_operation_add(pre_end_time, min_trans_time) # Add Transportation Time
                    else:
                        cur_start_time = pre_end_time
                    cur_start_time = open_start_time if TimeUtils.compare_times_later(open_start_time, pre_end_time) > 0 else pre_end_time
                    if cur_start_time == "00:00" or cur_start_time == "":
                        cur_start_time = "08:00"
                    min_cur_end_time = TimeUtils.time_operation_add(cur_start_time, min_hours)  # 
                    max_cur_end_time = TimeUtils.time_operation_add(cur_start_time, max_hours)


                    # If the earliest end time is later than the close time, it is considered a violation
                    if TimeUtils.compare_times_later(min_cur_end_time, open_end_time) > 0:
                        violations.append(f"chronological_violation: POI {activity['id']} occurs after close time {open_end_time} on day {day_key}")
                    
                    # Check if the POI activity time is reasonable (the start time cannot be later than the end time)
                    if cur_start_time and min_cur_end_time and TimeUtils.compare_times_later(cur_start_time, open_end_time) > 0:
                        violations.append(f"chronological_violation: POI {activity['id']} start time {cur_start_time} is after end time {min_cur_end_time} on day {day_key}")
                    
                    cur_end_time = min_cur_end_time if TimeUtils.compare_times_later(open_end_time, min_cur_end_time) > 0 else open_end_time
                    
                    # Record activity time for overlap check
                    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_key
                        })

                    pre_lat, pre_lon = poi_lat, poi_lon
                    pre_start_time, pre_end_time = cur_start_time, cur_end_time 

                elif activity['type'] == 'hotel':
                    hotel_info = self.poi_analyzer.get_hotel_info(activity['id'], poi_dict['locale'])
                    # Update the latitude and longitude to the hotel's latitude and longitude
                    if 'lat' in hotel_info and 'lon' in hotel_info:
                        hotel_lat, hotel_lon = hotel_info['lat'], hotel_info['lon']
                        if pre_lat and pre_lon:
                            min_trans_time = self.poi_analyzer.calculate_transportation_time_by_lat_lon(pre_lat, pre_lon, hotel_lat, hotel_lon) 
                            cur_start_time, cur_end_time = TimeUtils.time_operation_add(pre_end_time, min_trans_time), TimeUtils.time_operation_add(pre_end_time, min_trans_time)  
                        elif day_key==0:  # First Day
                            cur_start_time, cur_end_time = TimeUtils.time_operation_add(pre_end_time, 1), TimeUtils.time_operation_add(pre_end_time, 2)  # The first day arrives at the hotel, the airport distance to the hotel traffic time is 1 hour, stays in the hotel for 1 hour
                        elif len(pre_start_time) == 0:
                            cur_start_time, cur_end_time = "08:00","08:00"  # Some day starts from the hotel
                        else:
                            cur_start_time, cur_end_time = pre_end_time, pre_end_time
                        activity_times.append({
                            'type': 'hotel',
                            'id': activity['id'],
                            'start': cur_start_time,
                            'end': cur_end_time,
                            'day': day_key
                        })
                        pre_lat, pre_lon = hotel_lat, hotel_lon
                        pre_start_time, pre_end_time = cur_start_time, cur_end_time  

            # Add the activity time of the day to the global list
            all_activity_times.extend(activity_times)

            # Check activity time overlap
            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 time overlap
                    if (TimeUtils.compare_times_later(time1['start'], time2['end']) < 0 and 
                        TimeUtils.compare_times_later(time2['start'], time1['end']) < 0):
                        violations.append(f"chronological_violation: Activities {time1['id']} and {time2['id']} overlap on day {day_key}")
            
        return violations
    
    def _check_location_consistency(self, itinerary: Dict,poi_dict: Dict) -> List[str]:
        """Check Location Consistency"""
        violations = []
        current_location = poi_dict.get("start_city", "") if poi_dict.get("start_city", "") else poi_dict.get("departure", "")
        current_lat, current_lon = 0,0
        id_have = []
        # Check if the activities of each day are in the city where the traveler is located
        for day_data in itinerary['dayInfos']:
            if not isinstance(day_data, dict):
                continue
            
            day_key = day_data.get("day", 0)
            scheduleDetail = day_data.get("scheduleDetail", [])
            for schedule in scheduleDetail: 
                detailList = schedule.get("detailList", [])
                for detail in detailList:

                    if detail['type'] == "transportation" and detail['id'] not in id_have:
                        for transportation in self.poi_analyzer.transportation_info:
                            if detail['id'] == transportation['planid']:
                                if transportation['key'].split('->')[0] != current_location and current_location not in transportation['key'].split('->')[0]:

                                    violations.append(f"location_violation: TransportationID {transportation['planid']} is in {transportation['key'].split('->')[0]}, not in {current_location} on day {day_key}")

                                current_location = transportation['key'].split('->')[1] # Transportation, update the current location
                                try:
                                    current_lat, current_lon = transportation['to']['lat'], transportation['to']['lng']
                                except:
                                    current_lat, current_lon = 0,0
                                id_have.append(detail['id'])
                                break

                    elif detail['type'] == "poi":  # There is a case of pushing nearby attractions
                        poi_api_info, min_spend_time, poi_info= self.poi_analyzer.read_poi_api_info(detail.get("id"), poi_dict['locale'])
                        if not poi_api_info:
                            continue
                        location = poi_api_info.get("districtName", "")
                        poi_lat, poi_lon = poi_api_info.get("lat", 0), poi_api_info.get("lon", 0)
                        if current_lat and current_lon and poi_lat and poi_lon:
                            distance = self.poi_analyzer.calculate_distance(current_lat, current_lon, poi_lat, poi_lon)
                            # print("Attraction distance:", distance)
                        else:
                            distance = 100
                        if location != current_location and distance > 300:
                            # There is a case of pushing nearby attractions, need to calculate the distance
                            violations.append(f"location_violation: POIID {detail['id']} is in {location}, not in {current_location} and away from {current_location} {distance:.1f} km on day {day_key}")

                    elif detail['type'] == "hotel":
                        hotel_info = self.poi_analyzer.get_hotel_info(detail['id'], poi_dict['locale'])
                        location = hotel_info['cityName']
                        hotel_lat, hotel_lon = hotel_info['lat'], hotel_info['lon']
                        if current_lat and current_lon:
                            distance = self.poi_analyzer.calculate_distance(current_lat, current_lon, hotel_lat, hotel_lon)
                        else:
                            distance = 100
                        if location != current_location and distance > 300:
                            violations.append(f"location_violation: HotelID {detail['id']} is in {location} but is not in {current_location} on day {day_key}")
                        # Update the latitude and longitude to the hotel's latitude and longitude
                        current_lat, current_lon = hotel_lat, hotel_lon

        return violations
    
    def _check_operating_hours(self, itinerary: Dict, poi_dict: Dict) -> List[str]:
        """Check Operating Hours"""
        violations = []
        id_have = []
        for day_data in itinerary['dayInfos']:
            if not isinstance(day_data, dict):
                continue
                
            day_key = day_data.get("day", 0)
            scheduleDetail = day_data.get("scheduleDetail", [])
            
            pre_lat, pre_lon = 0,0  # Initialize latitude and longitude variables
            for schedule in scheduleDetail:
                detailList = schedule.get("detailList", [])
                period = schedule.get("period", "")
                initial_start_time = "08:00" if period in ["Morning", "上午"] else "13:30" if period in ["Afternoon", "下午"] else "19:30" if period in ["Evening", "晚上"] else "08:00"
                pre_min_start_time, pre_min_end_time, pre_max_start_time, pre_max_end_time = initial_start_time, initial_start_time, initial_start_time, initial_start_time  # Initialize time variables
                for detail in detailList:
                    if detail['type'] == 'transportation' and detail['id'] not in id_have:
                        for transportation in self.poi_analyzer.transportation_info:
                            if detail['id'] == transportation['planid']:
                                cur_start_time, cur_end_time = (TimeUtils.extract_time_only(transportation['depature_time']),TimeUtils.extract_time_only(transportation['flight_time'])) 
                                pre_min_start_time, pre_min_end_time, pre_max_start_time, pre_max_end_time = cur_start_time, cur_end_time, cur_start_time, cur_end_time 
                                break
                        id_have.append(detail['id'])
                    elif detail['type'] == 'poi': 
                        poi_api_info, min_spend_time, poi_info= self.poi_analyzer.read_poi_api_info(detail['id'], poi_dict['locale'])
                        if not poi_api_info:
                            continue

                        open_time = self.poi_analyzer.parse_poi_time_window(poi_info)
                        open_start_time, open_end_time = (open_time.split('-')[0].strip(),open_time.split('-')[1].strip())  
                
                        poi_lat, poi_lon = poi_api_info.get("lat", 0), poi_api_info.get("lon", 0)
                        if pre_lat and pre_lon:
                            min_trans_time = self.poi_analyzer.calculate_transportation_time_by_lat_lon(pre_lat, pre_lon, poi_lat, poi_lon) 
                            cur_min_start_time = TimeUtils.time_operation_add(pre_min_end_time, min_trans_time) # Add Transportation Time
                            cur_max_start_time = TimeUtils.time_operation_add(pre_max_end_time, min_trans_time)
                        else:
                            cur_min_start_time, cur_max_start_time = open_start_time, open_start_time

                        if cur_min_start_time == "00:00":
                            cur_min_start_time =initial_start_time
                        if cur_max_start_time == "00:00":
                            cur_max_start_time = initial_start_time
                        
                        # print(f"POI {detail['id']} open time is {open_time}, and max arrive time is {cur_max_start_time} on day {day_key} \nbut min arrive time is {cur_min_start_time} on day {day_key}")

                        if TimeUtils.compare_times_later(cur_min_start_time, open_end_time) > 0: # The earliest arrival time is later than the close time
                            violations.append(f"hours_violation: POI {detail['id']} open time is {open_time}, but arrive time is {cur_min_start_time} on day {day_key}")
                        
                        # If the latest arrival time is earlier than the open time by 2 hours
                        temp_cur_max_start_time = TimeUtils.time_operation_add(cur_max_start_time, 2)
                        if TimeUtils.compare_times_later(temp_cur_max_start_time, open_start_time) < 0:
                            violations.append(f"hours_violation: POI {detail['id']} open time is {open_time}, but arrive time is {cur_max_start_time} on day {day_key}")
                

                        if self.poi_analyzer.check_period_poiwindow(period,open_time):
                            violations.append(f"hours_violation: POI {detail['id']} open time is {open_time}, but period is {period} on day {day_key}")

                        min_hours, max_hours = self.poi_analyzer.calculate_poi_hours(poi_api_info)  # Minimum and Maximum Time for Playing
                        cur_min_start_time = open_start_time if TimeUtils.compare_times_later(open_start_time, cur_min_start_time) > 0 else cur_min_start_time
                        cur_max_start_time = open_start_time if TimeUtils.compare_times_later(open_start_time, cur_max_start_time) > 0 else cur_max_start_time
                        
                        min_cur_end_time = TimeUtils.time_operation_add(cur_min_start_time, min_hours)  
                        max_cur_end_time = TimeUtils.time_operation_add(cur_max_start_time, max_hours)
                        cur_min_end_time = min_cur_end_time if TimeUtils.compare_times_later(open_end_time, min_cur_end_time) > 0 else open_end_time
                        cur_max_end_time = max_cur_end_time if TimeUtils.compare_times_later(open_end_time, max_cur_end_time) > 0 else open_end_time

                        pre_lat, pre_lon = poi_lat, poi_lon
                        pre_min_start_time, pre_min_end_time, pre_max_start_time, pre_max_end_time = cur_min_start_time, cur_min_end_time, cur_max_start_time, cur_max_end_time  

                    elif detail['type'] == 'hotel':
                        hotel_info = self.poi_analyzer.get_hotel_info(detail['id'], poi_dict['locale'])
                        # Update the latitude and longitude to the hotel's latitude and longitude
                        current_lat, current_lon = hotel_info['lat'], hotel_info['lon']
                        if pre_lat and pre_lon:
                            min_trans_time = self.poi_analyzer.calculate_transportation_time_by_lat_lon(pre_lat, pre_lon, current_lat, current_lon) 
                            cur_min_start_time, cur_min_end_time = TimeUtils.time_operation_add(pre_min_end_time, min_trans_time), "23:59" 
                            cur_max_start_time, cur_max_end_time = TimeUtils.time_operation_add(pre_max_end_time, min_trans_time), "23:59" 
                        elif day_key==0:  # First Day
                            cur_min_start_time, cur_min_end_time = TimeUtils.time_operation_add(pre_min_end_time, 1), TimeUtils.time_operation_add(pre_min_end_time, 2)  # The first day arrives at the hotel, the airport distance to the hotel traffic time is 1 hour, stays in the hotel for 1 hour
                            cur_max_start_time, cur_max_end_time = TimeUtils.time_operation_add(pre_max_end_time, 1), TimeUtils.time_operation_add(pre_max_end_time, 2)  # The first day arrives at the hotel, the airport distance to the hotel traffic time is 1 hour, stays in the hotel for 1 hour
                        elif len(pre_min_start_time) == 0:
                            cur_min_start_time, cur_min_end_time =initial_start_time,initial_start_time  # Some day starts from the hotel
                            cur_max_start_time, cur_max_end_time =initial_start_time,initial_start_time  
                        else:
                            cur_min_start_time, cur_min_end_time = pre_min_end_time, pre_min_end_time
                            cur_max_start_time, cur_max_end_time = pre_max_end_time, pre_max_end_time

                        pre_lat, pre_lon = current_lat, current_lon
                        pre_min_start_time, pre_min_end_time, pre_max_start_time, pre_max_end_time = cur_min_start_time, cur_min_end_time, cur_max_start_time, cur_max_end_time  

        return violations
    
    def _check_travel_blockout(self, itinerary: Dict) -> List[str]:
        """Check Travel Time Blockout, cannot arrange activities before reaching the destination, and the expression description of transportation must match the transportation time"""
        violations = []
        id_have = []
        cross_transport = -1
        end_time_hour = 0
        cross_flight_day = 0
        for idx, day_data in enumerate(itinerary['dayInfos']):
            if not isinstance(day_data, dict):
                continue
                
            day_key = day_data.get("day", 0)
            scheduleDetail = day_data.get("scheduleDetail", [])
            
            for schedule in scheduleDetail:
                detailList = schedule.get("detailList", [])
                period = schedule.get("period", "")
                for detail in detailList:
                    if detail['type'] == 'transportation' and detail['id'] not in id_have:
                        for transportation in self.poi_analyzer.transportation_info:
                            if detail['id'] == transportation['planid']:
                                start_time = TimeUtils.parse_time(transportation['depature_time'])
                                end_time = TimeUtils.parse_time(transportation['flight_time'])
                                cross_transport = 1 if start_time.day != end_time.day else 0
                                end_time_hour = end_time.hour
                                cross_flight_day = idx + (end_time.day - start_time.day)
                                des_period = TimeUtils.get_time_of_day(transportation['depature_time'])[1]
                                if period not in des_period:
                                    violations.append(f"blockout_violation: Transportation {detail['id']} period is {des_period}, but itinerary period is {period} on day {day_key}")
                                break
                        id_have.append(detail['id'])
                    if cross_transport==0:  # Same day
                        if detail['type'] in ["poi", "hotel"]:
                            if end_time_hour>11 and period in ["Morning", "上午"]:
                                violations.append(f"blockout_violation: Transportation end time is {end_time_hour}, but arrange activity {detail['id']} is the {period} on day {day_key}")
                            if end_time_hour>17 and period in ["Morning", "Afternoon", "上午", "下午"]:
                                violations.append(f"blockout_violation: Transportation end time is {end_time_hour}, but arrange activity {detail['id']} is the {period} on day {day_key}")
                        if detail['type'] in ["poi"] and end_time_hour > 19 and period in ["Evening", "晚上"]:
                            violations.append(f"blockout_violation: Transportation end time is {end_time_hour}, but arrange activity {detail['id']} is the {period} on day {day_key}")
                        end_time_hour = 0
                        cross_flight_day  = 0
                    else:
                        if idx < cross_flight_day:
                            if detail['type'] in ["poi", "hotel"]:
                                violations.append(f"blockout_violation: Transportation is cross flight day on day {day_key}, but arrange activity {detail['id']} on day {day_key}")
                        elif idx == cross_flight_day:
                            if detail['type'] in ["poi", "hotel"]:
                                if end_time_hour>11 and period in ["Morning", "上午"]:
                                    violations.append(f"blockout_violation: Transportation  is cross flight day on day {day_key}, end time is {end_time_hour}, but arrange activity {detail['id']} is the {period} on day {day_key}")
                                if end_time_hour>17 and period in ["Morning", "Afternoon", "上午", "下午"]:
                                    violations.append(f"blockout_violation: Transportation is cross flight day on day {day_key}, end time is {end_time_hour}, but arrange activity {detail['id']} is the {period} on day {day_key}")
                            if detail['type'] in ["poi"] and end_time_hour > 19 and period in ["Evening", "晚上"]:
                                violations.append(f"blockout_violation: Transportation is cross flight day on day {day_key}, end time is {end_time_hour}, but arrange activity {detail['id']} is the {period} on day {day_key}")

        
        return violations
    
    def _check_early_transport_rule(self, itinerary: Dict) -> List[str]:
        """Check Early Transport Rule"""
        violations = []
        id_have = []
        for day_data in itinerary['dayInfos']:
            if not isinstance(day_data, dict):
                continue
                
            day_key = day_data.get("day", 0)
            scheduleDetail = day_data.get("scheduleDetail", [])
            
            # Collect activities before 10:00 on the day
            activity = []
            
            for schedule in scheduleDetail:
                detailList = schedule.get("detailList", [])
                for detail in detailList:
                    if detail['type'] == 'transportation' and detail['id'] not in id_have:
                        for transportation in self.poi_analyzer.transportation_info:
                            if detail['id'] == transportation['planid']:
                                departure_time = TimeUtils.extract_time_only(transportation['depature_time'])
                                if departure_time:
                                    departure_hour = TimeUtils.extract_time_for_comparison(departure_time) // 60
                                if departure_hour <= 10 and activity:
                                    violations.append(f"early_transport_violation: {len(activity)} Activity before early transport {detail['id']} on day {day_key}")
                                    break
                        id_have.append(detail['id'])
                        break
                    else:
                        activity.append(detail)


        return violations
    
    def _check_transport_continuity(self, itinerary: Dict) -> List[str]:
        """Check Transportation Continuity"""
        violations = []
        
        # Check if the transportation is arranged in order, avoid jumping or repeating routes
        transport_sequence = []
        id_have = []
        for day_data in itinerary['dayInfos']:
            if not isinstance(day_data, dict):
                continue
                
            day_key = day_data.get("day", 0)
            scheduleDetail = day_data.get("scheduleDetail", [])
            
            
            for schedule in scheduleDetail:
                detailList = schedule.get("detailList", [])
                for detail in detailList:
                    if detail['type'] == 'transportation' and detail['id'] not in id_have:
                        for transportation in self.poi_analyzer.transportation_info:
                            if detail['id'] == transportation['planid']:
                                transport_sequence.append({
                                    "day": day_key,
                                    "from": transportation['key'].split('->')[0],
                                    "to": transportation['key'].split('->')[1],
                                    "departure_time": transportation.get("depature_time", ""),
                                    "id": detail['id']
                                })
                                id_have.append(detail['id'])
                                break
        
        # Check continuity
        for i in range(len(transport_sequence) - 1):
            current = transport_sequence[i]
            next_transport = transport_sequence[i + 1]
            
            if current["to"] != next_transport["from"]:
                violations.append(f"continuity_violation: Discontinuous transport from {current['to']} to {next_transport['from']} between {current['id']} and {next_transport['id']}")
        
        return violations 