import logging
from fastapi import HTTPException, APIRouter

from ..structs.landmark import Landmark
from ..structs.linked_landmarks import LinkedLandmarks
from ..structs.trip import Trip
from ..landmarks.landmarks_manager import LandmarkManager
from ..optimization.optimizer import Optimizer
from ..optimization.refiner import Refiner
from ..cache import client as cache_client


logger = logging.getLogger(__name__)


manager = LandmarkManager()
optimizer = Optimizer()
refiner = Refiner(optimizer=optimizer)


# Initialize the API router
router = APIRouter()


#### For already existing trips/landmarks
@router.get("/trip/{trip_uuid}")
def get_trip(trip_uuid: str) -> Trip:
    """
    Look-up the cache for a trip that has been previously generated using its identifier.

    Args:
        trip_uuid (str) : unique identifier for a trip.

    Returns:
        (Trip)          : the corresponding trip.
    """
    try:
        trip = cache_client.get(f"trip_{trip_uuid}")
        return trip
    except KeyError as exc:
        logger.error(f"Failed to fetch trip with UUID {trip_uuid}: {str(exc)}")
        raise HTTPException(status_code=404, detail="Trip not found") from exc


# Fetch a landmark from memcached by its uuid
@router.get("/landmark/{landmark_uuid}")
def get_landmark(landmark_uuid: str) -> Landmark:
    """
    Returns a Landmark from its unique identifier.

    Args:
        landmark_uuid (str) : unique identifier for a Landmark.

    Returns:
        (Landmark)          : the corresponding Landmark.
    """
    try:
        landmark = cache_client.get(f"landmark_{landmark_uuid}")
        return landmark
    except KeyError as exc:
        logger.error(f"Failed to fetch landmark with UUID {landmark_uuid}: {str(exc)}")
        raise HTTPException(status_code=404, detail="Landmark not found") from exc


# Update the times between landmarks when removing an item from the list
@router.post("/trip/recompute-time/{trip_uuid}/{removed_landmark_uuid}")
def update_trip_time(trip_uuid: str, removed_landmark_uuid: str) -> Trip:
    """
    Updates the reaching times of a given trip when removing a landmark.

    Args:
        landmark_uuid (str) : unique identifier for a Landmark.

    Returns:
        (Landmark)          : the corresponding Landmark.
    """
    # First, fetch the trip in the cache.
    try:
        trip = cache_client.get(f'trip_{trip_uuid}')
    except KeyError as exc:
        logger.error(f"Failed to update trip with UUID {trip_uuid} (trip not found): {str(exc)}")
        raise HTTPException(status_code=404, detail='Trip not found') from exc

    landmarks = []
    next_uuid = trip.first_landmark_uuid

    # Extract landmarks
    try :
        while next_uuid is not None:
            landmark = cache_client.get(f'landmark_{next_uuid}')
            # Filter out the removed landmark.
            if next_uuid != removed_landmark_uuid :
                landmarks.append(landmark)
            next_uuid = landmark.next_uuid  # Prepare for the next iteration
    except KeyError as exc:
        logger.error(f"Failed to update trip with UUID {trip_uuid} : {str(exc)}")
        raise HTTPException(status_code=404, detail=f'landmark {next_uuid} not found') from exc

    # Re-link every thing and compute times again
    linked_tour = LinkedLandmarks(landmarks)
    trip = Trip.from_linked_landmarks(linked_tour, cache_client)

    return trip

