from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session, select
from typing import List, Optional
from sqlalchemy import func

from app.core.database import get_session
from app.models.route import Route
from app.models.route_stop import RouteStop
from app.schemas.route import RouteResponse, RouteCreate, RouteUpdate
from app.schemas.route_stop import RouteStopCreate, RouteStopUpdate
from app.api.deps import get_current_admin

router = APIRouter(prefix="/api/routes", tags=["routes"])


@router.get("", response_model=List[RouteResponse])
async def get_routes(
    origin_city: Optional[str] = Query(None),
    destination_city: Optional[str] = Query(None),
    session: Session = Depends(get_session)
):
    """Get all routes with optional filtering by origin and destination city."""
    statement = select(Route)
    
    if origin_city:
        statement = statement.where(Route.origin_city.contains(origin_city))
    
    if destination_city:
        statement = statement.where(Route.destination_city.contains(destination_city))
        
    routes = session.exec(statement).all()
    return routes


@router.get("/{route_id}", response_model=RouteResponse)
async def get_route(route_id: str, session: Session = Depends(get_session)):
    """Get a single route by ID."""
    route = session.get(Route, route_id)
    if not route:
        raise HTTPException(status_code=404, detail="Route not found")
    return route


@router.get("/{route_id}/stops")
async def get_route_stops(route_id: str, session: Session = Depends(get_session)):
    """Get all stops for a route."""
    from app.models.route_stop import RouteStop
    from app.models.bus_stop import BusStop
    
    statement = select(RouteStop, BusStop).join(
        BusStop, RouteStop.stop_id == BusStop.id
    ).where(RouteStop.route_id == route_id).order_by(RouteStop.stop_order)
    
    results = session.exec(statement).all()
    # Merge RouteStop data into BusStop response
    stops = []
    for route_stop, bus_stop in results:
        stop_data = bus_stop.model_dump()
        stop_data['stop_order'] = route_stop.stop_order
        stop_data['travel_time_minutes'] = route_stop.travel_time_minutes
        stop_data['stop_delay_minutes'] = route_stop.stop_delay_minutes
        stop_data['is_pickup_point'] = route_stop.is_pickup_point
        stop_data['is_dropoff_point'] = route_stop.is_dropoff_point
        stops.append(stop_data)
        
    return stops

@router.post("", response_model=RouteResponse)
async def create_route(
    route: RouteCreate,
    session: Session = Depends(get_session),
    _: bool = Depends(get_current_admin)
):
    """Create a new route (Admin only)."""
    db_route = Route.model_validate(route)
    session.add(db_route)
    session.commit()
    session.refresh(db_route)
    return db_route

@router.put("/{route_id}", response_model=RouteResponse)
async def update_route(
    route_id: str,
    route_update: RouteUpdate,
    session: Session = Depends(get_session),
    _: bool = Depends(get_current_admin)
):
    """Update a route (Admin only)."""
    db_route = session.get(Route, route_id)
    if not db_route:
        raise HTTPException(status_code=404, detail="Route not found")
    
    route_data = route_update.model_dump(exclude_unset=True)
    for key, value in route_data.items():
        setattr(db_route, key, value)
        
    session.add(db_route)
    session.commit()
    session.refresh(db_route)
    return db_route

@router.delete("/{route_id}")
async def delete_route(
    route_id: str,
    session: Session = Depends(get_session),
    _: bool = Depends(get_current_admin)
):
    """Delete a route (Admin only)."""
    db_route = session.get(Route, route_id)
    if not db_route:
        raise HTTPException(status_code=404, detail="Route not found")
        
    session.delete(db_route)
    session.commit()
    return {"ok": True}

# Route Stop Management with Cascade

@router.post("/{route_id}/stops")
async def add_stop_to_route(
    route_id: str,
    stop_data: RouteStopCreate,
    session: Session = Depends(get_session),
    _: bool = Depends(get_current_admin)
):
    """Add a stop to a route with cascading order adjustment."""
    # 1. Check if route exists
    route = session.get(Route, route_id)
    if not route:
        raise HTTPException(status_code=404, detail="Route not found")
        
    # 2. Determine stop order
    if stop_data.stop_order is None:
        # Append to end
        max_order = session.exec(select(func.max(RouteStop.stop_order)).where(RouteStop.route_id == route_id)).one()
        # handle case where max_order is None (no stops)
        stop_data.stop_order = (max_order or 0) + 1
    else:
        # Shift existing stops equal to or greater than new order
        existing_stops = session.exec(
            select(RouteStop).where(RouteStop.route_id == route_id, RouteStop.stop_order >= stop_data.stop_order)
        ).all()
        for stop in existing_stops:
            stop.stop_order += 1
            session.add(stop)
            
    # 3. Create new RouteStop
    new_stop = RouteStop(
        route_id=route_id,
        stop_id=stop_data.stop_id,
        stop_order=stop_data.stop_order,
        travel_time_minutes=stop_data.travel_time_minutes,
        stop_delay_minutes=stop_data.stop_delay_minutes or 0,
        is_pickup_point=stop_data.is_pickup_point,
        is_dropoff_point=stop_data.is_dropoff_point
    )
    session.add(new_stop)
    session.commit()
    session.refresh(new_stop)
    return new_stop

@router.put("/{route_id}/stops/{stop_id}")
async def update_route_stop_order(
    route_id: str,
    stop_id: str,
    update_data: RouteStopUpdate,
    session: Session = Depends(get_session),
    _: bool = Depends(get_current_admin)
):
    """Update a route stop, potentially reordering others."""
    # This assumes we find the connection by route_id and stop_id. 
    # NOTE: If a stop is on a route multiple times, this logic needs ID, but for now assuming unique stop per route.
    # Actually RouteStop has its own ID but we are using stop_id in path. Let's find the RouteStop entry.
    
    route_stop = session.exec(
        select(RouteStop).where(RouteStop.route_id == route_id, RouteStop.stop_id == stop_id)
    ).first()
    
    if not route_stop:
        raise HTTPException(status_code=404, detail="Stop not found on this route")

    old_order = route_stop.stop_order
    
    # Update fields
    if update_data.is_pickup_point is not None:
        route_stop.is_pickup_point = update_data.is_pickup_point
    if update_data.is_dropoff_point is not None:
        route_stop.is_dropoff_point = update_data.is_dropoff_point
    if update_data.travel_time_minutes is not None:
        route_stop.travel_time_minutes = update_data.travel_time_minutes
    if update_data.stop_delay_minutes is not None:
        route_stop.stop_delay_minutes = update_data.stop_delay_minutes
        
    # Reordering logic
    if update_data.stop_order is not None and update_data.stop_order != old_order:
        new_order = update_data.stop_order
        
        if new_order > old_order:
            # Moving down: shift stops between old+1 and new DOWN (-1)
            stops_to_shift = session.exec(
                select(RouteStop).where(
                    RouteStop.route_id == route_id,
                    RouteStop.stop_order > old_order,
                    RouteStop.stop_order <= new_order
                )
            ).all()
            for s in stops_to_shift:
                s.stop_order -= 1
                session.add(s)
        else:
            # Moving up: shift stops between new and old-1 UP (+1)
            stops_to_shift = session.exec(
                select(RouteStop).where(
                    RouteStop.route_id == route_id,
                    RouteStop.stop_order >= new_order,
                    RouteStop.stop_order < old_order
                )
            ).all()
            for s in stops_to_shift:
                s.stop_order += 1
                session.add(s)
                
        route_stop.stop_order = new_order

    session.add(route_stop)
    session.commit()
    session.refresh(route_stop)
    return route_stop

