from __future__ import annotations

from typing import Optional, TYPE_CHECKING
from .elevator_logic_base import ElevatorLogic

if TYPE_CHECKING:
    from ..elevator import Elevator
    from ..floor import Floor

class LookLogic(ElevatorLogic):
    def __init__(self, building):
        super().__init__(building)
        # Stores elevator "sweep" direction
        self.elevator_directions = {}

    def __repr__(self) -> str:
        return "LookLogic"

    def assign_elevator_targets(self) -> None:
        # For each elevator, update its target based on LOOK logic
        for elevator in self.building.elevators:
            # If elevator is at target or has no target, find next target
            if elevator.state == 'IDLE' and (elevator.target_floor is None or
                                             elevator.target_floor == elevator.current_floor):
                next_target = self._get_next_target_look(elevator)
                if next_target and next_target != elevator.current_floor:
                    elevator.target_floor = next_target
                elif not next_target and not elevator.onboard_passengers:
                    # No more targets and no passengers, clear target
                    elevator.target_floor = None

            # Even when moving, update target if there's a better stop in the same direction
            elif elevator.state in ['MOVING_UP', 'MOVING_DOWN'] and not elevator.is_at_capacity():
                # Check if there are waiting passengers in the current direction
                next_target = self._get_next_target_look(elevator)
                if next_target and next_target != elevator.current_floor:
                    # Update target if it's in the same direction and closer
                    current_floor_num = int(elevator.current_floor)
                    next_target_num = int(next_target)
                    current_target_num = int(elevator.target_floor) if elevator.target_floor else current_floor_num

                    if elevator.state == 'MOVING_UP':
                        # If new target is between current and old target, or beyond old target in same direction
                        if next_target_num > current_floor_num and next_target_num >= current_target_num:
                            elevator.target_floor = next_target
                    elif elevator.state == 'MOVING_DOWN':
                        # If new target is between current and old target, or beyond old target in same direction
                        if next_target_num < current_floor_num and next_target_num <= current_target_num:
                            elevator.target_floor = next_target

    def _get_next_target_look(self, elevator: Elevator) -> Optional[Floor]:
        current_floor_num = int(elevator.current_floor)

        # Determine direction from elevator state
        if elevator.state == 'MOVING_UP':
            direction = 'UP'
            self.elevator_directions[elevator.elevator_id] = 'UP'
        elif elevator.state == 'MOVING_DOWN':
            direction = 'DOWN'
            self.elevator_directions[elevator.elevator_id] = 'DOWN'
        else:  # IDLE
            # Try to maintain last direction if we have requests in that direction
            last_direction = self.elevator_directions.get(elevator.elevator_id, 'UP')
            direction = last_direction

        # Collect all destinations from onboard passengers
        dest_floors_up = []
        dest_floors_down = []

        for passenger in elevator.onboard_passengers:
            if passenger.destination_floor:
                dest_num = int(passenger.destination_floor)
                if dest_num > current_floor_num:
                    dest_floors_up.append((passenger.destination_floor, dest_num))
                elif dest_num < current_floor_num:
                    dest_floors_down.append((passenger.destination_floor, dest_num))

        # Collect floors with waiting passengers based on their destination direction
        # Crucial for LOOK: only consider passengers whose trip direction we can serve
        if not elevator.is_at_capacity():
            for floor in self.building.floors:
                floor_num = int(floor)
                if floor.waiting_passengers:
                    # Check if any passenger on this floor wants to travel up or down
                    for passenger in floor.waiting_passengers:
                        if passenger.destination_floor:
                            dest_num = int(passenger.destination_floor)
                            # Passenger wants to go UP from their floor
                            if dest_num > floor_num > current_floor_num:
                                dest_floors_up.append((floor, floor_num))
                                break  # Only need to add floor once
                            # Passenger wants to go DOWN from their floor
                            if dest_num < floor_num < current_floor_num:
                                dest_floors_down.append((floor, floor_num))
                                break  # Only need to add floor once

        # LOOK logic: serve all in current direction, then reverse immediately
        result = None

        if direction == 'UP':
            if dest_floors_up:
                # Go to closest floor going up
                dest_floors_up.sort(key=lambda x: x[1])
                result = dest_floors_up[0][0]
            elif dest_floors_down:
                # No more destinations up, reverse immediately
                dest_floors_down.sort(key=lambda x: x[1], reverse=True)
                result = dest_floors_down[0][0]
                # Update direction since we're reversing
                self.elevator_directions[elevator.elevator_id] = 'DOWN'
        else:  # DOWN
            if dest_floors_down:
                # Go to closest floor going down
                dest_floors_down.sort(key=lambda x: x[1], reverse=True)
                result = dest_floors_down[0][0]
            elif dest_floors_up:
                # No more destinations down, reverse immediately
                dest_floors_up.sort(key=lambda x: x[1])
                result = dest_floors_up[0][0]
                # Update direction since we're reversing
                self.elevator_directions[elevator.elevator_id] = 'UP'

        return result
