"""
Driver Routes
Driver profile, documents, location, earnings, and ride management
"""
from typing import List, Optional
from datetime import datetime, date
from fastapi import APIRouter, Depends, HTTPException, status, Query, UploadFile, File, Form
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.orm import selectinload

from app.database import get_db
from app.models.user import User, UserRole
from app.models.driver import Driver, DriverDocument, DriverStatus, DocumentType
from app.models.driver_account_settings import DriverAccountSettings
from app.models.ride import Ride, RideStatus
from app.schemas.driver import (
    DriverResponse,
    DriverUpdate,
    DriverLocationUpdate,
    DriverDocumentResponse,
    DriverDocumentsUploadResponse,
    DriverEarningsResponse,
    DriverStatusUpdate,
    VehicleCreate,
    VehicleResponse,
)
from app.schemas.ride import RideListResponse, FareBidCreate, FareBidResponse, ActiveRideResponse, NearbyRidesResponse, NearbyRideItem
from app.schemas.common import MessageResponse
from app.schemas.driver_settings import DriverAccountSettingsResponse, DriverAccountSettingsUpdate
from app.services.ride_service import RideService
from app.utils.dependencies import get_current_user, require_role, get_current_driver
from app.routes.websocket import notify_driver_location, publish_ride_event
from app.media.driver_document import save_driver_document

router = APIRouter()


@router.get("/profile", response_model=DriverResponse)
async def get_driver_profile(
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Get driver profile with vehicle and documents"""
    # Load related data (documents has lazy="dynamic" - cannot use selectinload)
    query = select(Driver).where(Driver.id == driver.id).options(
        selectinload(Driver.user),
        selectinload(Driver.current_vehicle)
    )
    result = await db.execute(query)
    driver = result.scalar_one()
    
    # Load documents separately (dynamic relationship)
    doc_query = select(DriverDocument).where(DriverDocument.driver_id == driver.id)
    doc_result = await db.execute(doc_query)
    documents = doc_result.scalars().all()
    
    return _build_driver_profile_response(driver, documents)


@router.get("/account-settings", response_model=DriverAccountSettingsResponse)
async def get_account_settings(
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Get driver account settings"""
    query = select(DriverAccountSettings).where(DriverAccountSettings.driver_id == driver.id)
    result = await db.execute(query)
    settings = result.scalar_one_or_none()
    if not settings:
        settings = DriverAccountSettings(driver_id=driver.id, settings={})
        db.add(settings)
        await db.commit()
        await db.refresh(settings)
    return DriverAccountSettingsResponse(
        id=settings.id,
        driver_id=settings.driver_id,
        settings=settings.settings or {}
    )


@router.put("/account-settings", response_model=DriverAccountSettingsResponse)
async def update_account_settings(
    data: DriverAccountSettingsUpdate,
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Update driver account settings"""
    query = select(DriverAccountSettings).where(DriverAccountSettings.driver_id == driver.id)
    result = await db.execute(query)
    settings = result.scalar_one_or_none()
    if not settings:
        settings = DriverAccountSettings(driver_id=driver.id, settings=data.settings or {})
        db.add(settings)
    else:
        settings.settings = data.settings if data.settings is not None else settings.settings or {}
    await db.commit()
    await db.refresh(settings)
    return DriverAccountSettingsResponse(
        id=settings.id,
        driver_id=settings.driver_id,
        settings=settings.settings or {}
    )


@router.put("/profile", response_model=DriverResponse)
async def update_driver_profile(
    data: DriverUpdate,
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Update driver profile"""
    update_data = data.model_dump(exclude_unset=True)

    # Update user-level fields
    full_name = update_data.pop("full_name", None)
    if full_name is not None:
        name = full_name.strip()
        if name:
            parts = name.split(" ", 1)
            driver.user.first_name = parts[0]
            driver.user.last_name = parts[1] if len(parts) > 1 else None
        else:
            driver.user.first_name = None
            driver.user.last_name = None

    email = update_data.pop("email", None)
    if email is not None:
        driver.user.email = str(email).strip().lower() if str(email).strip() else None

    profile_picture = update_data.pop("profile_picture", None)
    if profile_picture is not None:
        driver.user.profile_picture = profile_picture

    # Update driver-level fields
    for field, value in update_data.items():
        if hasattr(driver, field):
            setattr(driver, field, value)
    
    await db.commit()
    await db.refresh(driver)

    # Return the same shape as GET /driver/profile to avoid response mismatch.
    query = select(Driver).where(Driver.id == driver.id).options(
        selectinload(Driver.user),
        selectinload(Driver.current_vehicle)
    )
    result = await db.execute(query)
    refreshed_driver = result.scalar_one()

    doc_query = select(DriverDocument).where(DriverDocument.driver_id == driver.id)
    doc_result = await db.execute(doc_query)
    documents = doc_result.scalars().all()

    return _build_driver_profile_response(refreshed_driver, documents)


@router.put("/location", response_model=MessageResponse)
async def update_location(
    data: DriverLocationUpdate,
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Update driver's current location"""
    driver.current_latitude = data.latitude
    driver.current_longitude = data.longitude
    driver.heading = data.heading
    driver.location_updated_at = datetime.utcnow()
    
    await db.commit()
    if driver.is_on_ride:
        active_ride_query = select(Ride).where(
            Ride.driver_id == driver.id,
            Ride.status.in_([RideStatus.ACCEPTED, RideStatus.DRIVER_ARRIVED, RideStatus.STARTED]),
        ).order_by(Ride.created_at.desc()).limit(1)
        active_ride_result = await db.execute(active_ride_query)
        active_ride = active_ride_result.scalar_one_or_none()
        if active_ride:
            await notify_driver_location(
                active_ride.id,
                data.latitude,
                data.longitude,
                data.heading,
            )
    return MessageResponse(message="Location updated")


@router.put("/status", response_model=MessageResponse)
async def update_online_status(
    data: DriverStatusUpdate,
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Toggle driver online/offline status"""
    # Only restrict going online. Drivers should always be able to go offline.
    if data.is_online and driver.status != DriverStatus.APPROVED:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Driver is not approved to go online"
        )
    
    driver.is_online = data.is_online
    if data.is_online:
        driver.last_online_at = datetime.utcnow()
    
    await db.commit()
    return MessageResponse(message=f"Status updated to {'online' if data.is_online else 'offline'}")


@router.post("/documents", response_model=DriverDocumentsUploadResponse)
async def upload_document(
    profile_photo: Optional[UploadFile] = File(None),
    drivers_license: Optional[UploadFile] = File(None),
    cnic: Optional[UploadFile] = File(None),
    vehicle_document: Optional[UploadFile] = File(None),
    license_number: Optional[str] = Form(None),
    license_expiry: Optional[date] = Form(None),
    cnic_number: Optional[str] = Form(None),
    vehicle_document_number: Optional[str] = Form(None),
    vehicle_document_expiry: Optional[date] = Form(None),
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Upload multiple driver verification documents in one multipart/form-data request"""
    uploads = [
        (DocumentType.PROFILE_PHOTO, profile_photo, None, None),
        (DocumentType.DRIVERS_LICENSE, drivers_license, license_number, license_expiry),
        (DocumentType.NATIONAL_ID, cnic, cnic_number, None),
        (DocumentType.VEHICLE_REGISTRATION, vehicle_document, vehicle_document_number, vehicle_document_expiry),
    ]

    provided_uploads = [item for item in uploads if item[1] is not None]
    if not provided_uploads:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="At least one file must be uploaded"
        )

    saved_documents: List[DriverDocument] = []
    for document_type, upload, document_number, expiry_date_value in provided_uploads:
        try:
            document_url = await save_driver_document(driver.id, document_type, upload)
        except ValueError as exc:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"{document_type.value}: {exc}"
            ) from exc

        existing_query = select(DriverDocument).where(
            DriverDocument.driver_id == driver.id,
            DriverDocument.document_type == document_type,
        )
        existing_result = await db.execute(existing_query)
        document = existing_result.scalar_one_or_none()

        if document is None:
            document = DriverDocument(
                driver_id=driver.id,
                document_type=document_type,
                document_url=document_url,
                document_number=document_number,
                expiry_date=expiry_date_value,
            )
            db.add(document)
        else:
            document.document_url = document_url
            document.document_number = document_number
            document.expiry_date = expiry_date_value
            document.is_verified = False
            document.verified_at = None
            document.rejection_reason = None

        saved_documents.append(document)

    await db.commit()
    for document in saved_documents:
        await db.refresh(document)

    return DriverDocumentsUploadResponse(
        message="Documents uploaded successfully",
        documents=[DriverDocumentResponse.model_validate(document) for document in saved_documents]
    )


@router.get("/earnings", response_model=DriverEarningsResponse)
async def get_earnings(
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Get driver earnings summary"""
    from app.models.payment import WithdrawalRequest, WithdrawalStatus
    from sqlalchemy import func
    from datetime import timedelta
    
    now = datetime.utcnow()
    today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
    week_start = today_start - timedelta(days=7)
    month_start = today_start - timedelta(days=30)
    
    # Pending withdrawals
    pending_query = select(func.sum(WithdrawalRequest.amount)).where(
        WithdrawalRequest.driver_id == driver.id,
        WithdrawalRequest.status.in_([WithdrawalStatus.PENDING, WithdrawalStatus.PROCESSING])
    )
    pending_result = await db.execute(pending_query)
    pending_withdrawals = pending_result.scalar() or 0
    
    # Today's earnings and rides
    today_rides_query = select(func.count(), func.sum(Ride.driver_earnings)).where(
        Ride.driver_id == driver.id,
        Ride.status == RideStatus.COMPLETED,
        Ride.completed_at >= today_start
    )
    today_result = await db.execute(today_rides_query)
    today_data = today_result.one()
    
    return DriverEarningsResponse(
        total_earnings=driver.total_earnings,
        current_balance=driver.current_balance,
        pending_withdrawals=pending_withdrawals,
        today_earnings=today_data[1] or 0,
        this_week_earnings=0,  # TODO: Calculate
        this_month_earnings=0,
        completed_rides_today=today_data[0] or 0,
        completed_rides_this_week=0,
        completed_rides_this_month=0
    )


def _build_driver_profile_response(driver: Driver, documents: List[DriverDocument]) -> DriverResponse:
    """Build a consistent DriverResponse payload for GET/PUT profile."""
    return DriverResponse(
        id=driver.id,
        user_id=driver.user_id,
        full_name=driver.user.full_name,
        email=driver.user.email,
        phone=driver.user.phone,
        profile_picture=driver.user.profile_picture,
        license_number=driver.license_number,
        license_expiry=driver.license_expiry,
        status=driver.status,
        is_online=driver.is_online,
        is_on_ride=driver.is_on_ride,
        current_latitude=driver.current_latitude,
        current_longitude=driver.current_longitude,
        location_updated_at=driver.location_updated_at,
        average_rating=driver.average_rating,
        total_ratings=driver.total_ratings,
        total_rides=driver.total_rides,
        completed_rides=driver.completed_rides,
        total_earnings=driver.total_earnings,
        current_balance=driver.current_balance,
        current_vehicle=VehicleResponse.model_validate(driver.current_vehicle) if driver.current_vehicle else None,
        documents=[DriverDocumentResponse.model_validate(d) for d in documents],
        created_at=driver.created_at
    )


@router.get("/rides/nearby", response_model=NearbyRidesResponse)
async def get_nearby_ride_requests(
    limit: int = Query(20, ge=1, le=100),
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Get nearby ride requests available for bidding"""
    if not driver.is_online or driver.is_on_ride:
        return NearbyRidesResponse(rides=[])
    
    if not driver.current_latitude or not driver.current_longitude:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Location not available. Please update your location."
        )
    
    # TODO: Use geospatial query for nearby rides
    query = select(Ride).where(
        Ride.status.in_([RideStatus.REQUESTED, RideStatus.BIDDING]),
        Ride.driver_id.is_(None)
    ).order_by(Ride.created_at.desc()).limit(limit)
    
    result = await db.execute(query)
    rides = result.scalars().all()
    
    return NearbyRidesResponse(rides=[
        NearbyRideItem(
            id=ride.id,
            ride_code=ride.ride_code,
            pickup_address=ride.pickup_address,
            dropoff_address=ride.dropoff_address,
            estimated_fare=ride.estimated_fare,
            estimated_distance_km=ride.estimated_distance_km,
            passenger_count=ride.passenger_count,
            vehicle_category_id=ride.vehicle_category_id
        )
        for ride in rides
    ])


@router.post("/rides/{ride_id}/bid", response_model=FareBidResponse)
async def submit_fare_bid(
    ride_id: int,
    data: FareBidCreate,
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Submit a fare bid for a ride request"""
    if driver.status != DriverStatus.APPROVED:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Driver is not approved"
        )
    
    if not driver.is_online:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="You must be online to submit bids"
        )
    
    data.ride_id = ride_id
    try:
        bid = await RideService.submit_bid(db, driver.id, data)
        ride_query = select(Ride).where(Ride.id == bid.ride_id)
        ride_result = await db.execute(ride_query)
        ride = ride_result.scalar_one_or_none()
        if ride:
            await publish_ride_event(
                ride,
                "bid_submitted",
                {
                    "driver_id": driver.id,
                    "driver_name": driver.user.full_name,
                    "bid_id": bid.id,
                    "bid_amount": bid.bid_amount,
                    "estimated_arrival_minutes": bid.estimated_arrival_minutes,
                },
            )
        
        return FareBidResponse(
            id=bid.id,
            ride_id=bid.ride_id,
            driver_id=bid.driver_id,
            driver_name=driver.user.full_name,
            driver_photo=driver.user.profile_picture,
            driver_rating=driver.average_rating,
            driver_total_rides=driver.total_rides,
            bid_amount=bid.bid_amount,
            original_fare=bid.original_fare,
            bid_percentage=bid.bid_percentage,
            status=bid.status,
            message=bid.message,
            estimated_arrival_minutes=bid.estimated_arrival_minutes,
            submitted_at=bid.submitted_at,
            expires_at=bid.expires_at
        )
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))


@router.get("/rides/active", response_model=ActiveRideResponse)
async def get_active_ride(
    driver: Driver = Depends(get_current_driver),
    db: AsyncSession = Depends(get_db)
):
    """Get driver's current active ride"""
    query = select(Ride).where(
        Ride.driver_id == driver.id,
        Ride.status.in_([
            RideStatus.ACCEPTED,
            RideStatus.DRIVER_ARRIVED,
            RideStatus.STARTED
        ])
    )
    
    result = await db.execute(query)
    ride = result.scalar_one_or_none()
    
    if not ride:
        return ActiveRideResponse(active_ride=None, ride_code=None, status=None)
    
    return ActiveRideResponse(
        active_ride=ride.id,
        ride_code=ride.ride_code,
        status=ride.status.value if hasattr(ride.status, "value") else str(ride.status)
    )
