from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from sqlalchemy import func, and_, desc from datetime import datetime, timedelta from typing import List, Optional from pydantic import BaseModel from ..database import get_db from ..models import ( User, Car, Inquiry, InquiryStatus, VehicleRequest, RequestVehicle, PurchasedVehicle, DealerApplication, DealerInfo, VehicleShare, ShareReward, WithdrawalRequest, ReferralReward, HeroBanner, ChargeHistory, ) from .auth import get_current_admin_user router = APIRouter(prefix="/dashboard", tags=["Dashboard"]) class DashboardStats(BaseModel): total_users: int new_users_today: int new_users_this_week: int total_dealers: int pending_dealer_applications: int total_cars: int total_vehicle_requests: int pending_requests: int total_purchased_vehicles: int total_inquiries: int pending_inquiries: int total_shares: int purchased_shares: int total_withdrawals: int pending_withdrawals: int total_cc_charged: float total_withdrawal_amount: float class RevenueStats(BaseModel): total_revenue: float revenue_this_month: float revenue_last_month: float platform_commission: float dealer_commission: float class ChartData(BaseModel): labels: List[str] values: List[int] class DailyStats(BaseModel): date: str users: int requests: int purchases: int revenue: float class RecentActivity(BaseModel): type: str title: str description: str time: str icon: str class TopDealer(BaseModel): id: int name: str dealer_code: str total_sales: int total_commission: float @router.get("/stats", response_model=DashboardStats) def get_dashboard_stats( db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user) ): """Get comprehensive dashboard statistics""" today = datetime.utcnow().date() week_ago = today - timedelta(days=7) # User stats total_users = db.query(func.count(User.id)).filter(User.is_admin == False).scalar() or 0 new_users_today = db.query(func.count(User.id)).filter( and_( User.is_admin == False, func.date(User.created_at) == today ) ).scalar() or 0 new_users_this_week = db.query(func.count(User.id)).filter( and_( User.is_admin == False, func.date(User.created_at) >= week_ago ) ).scalar() or 0 # Dealer stats total_dealers = db.query(func.count(DealerInfo.id)).filter(DealerInfo.is_active == True).scalar() or 0 pending_dealer_applications = db.query(func.count(DealerApplication.id)).filter( DealerApplication.status == "pending" ).scalar() or 0 # Car stats total_cars = db.query(func.count(Car.id)).scalar() or 0 # Vehicle request stats total_vehicle_requests = db.query(func.count(VehicleRequest.id)).scalar() or 0 pending_requests = db.query(func.count(VehicleRequest.id)).filter( VehicleRequest.status == "pending" ).scalar() or 0 # Purchased vehicles total_purchased_vehicles = db.query(func.count(PurchasedVehicle.id)).scalar() or 0 # Inquiry stats total_inquiries = db.query(func.count(Inquiry.id)).scalar() or 0 pending_inquiries = db.query(func.count(Inquiry.id)).filter( Inquiry.status == InquiryStatus.PENDING ).scalar() or 0 # Share stats total_shares = db.query(func.count(VehicleShare.id)).scalar() or 0 purchased_shares = db.query(func.count(VehicleShare.id)).filter( VehicleShare.is_purchased == True ).scalar() or 0 # Withdrawal stats total_withdrawals = db.query(func.count(WithdrawalRequest.id)).scalar() or 0 pending_withdrawals = db.query(func.count(WithdrawalRequest.id)).filter( WithdrawalRequest.status == "pending" ).scalar() or 0 # CC stats total_cc_charged = db.query(func.coalesce(func.sum(ChargeHistory.amount), 0)).filter( ChargeHistory.status == "completed" ).scalar() or 0 total_withdrawal_amount = db.query(func.coalesce(func.sum(WithdrawalRequest.amount), 0)).filter( WithdrawalRequest.status == "completed" ).scalar() or 0 return DashboardStats( total_users=total_users, new_users_today=new_users_today, new_users_this_week=new_users_this_week, total_dealers=total_dealers, pending_dealer_applications=pending_dealer_applications, total_cars=total_cars, total_vehicle_requests=total_vehicle_requests, pending_requests=pending_requests, total_purchased_vehicles=total_purchased_vehicles, total_inquiries=total_inquiries, pending_inquiries=pending_inquiries, total_shares=total_shares, purchased_shares=purchased_shares, total_withdrawals=total_withdrawals, pending_withdrawals=pending_withdrawals, total_cc_charged=float(total_cc_charged), total_withdrawal_amount=float(total_withdrawal_amount), ) @router.get("/revenue", response_model=RevenueStats) def get_revenue_stats( db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user) ): """Get revenue statistics""" today = datetime.utcnow().date() this_month_start = today.replace(day=1) last_month_end = this_month_start - timedelta(days=1) last_month_start = last_month_end.replace(day=1) # Total CC charged as revenue total_revenue = db.query(func.coalesce(func.sum(ChargeHistory.amount), 0)).filter( ChargeHistory.status == "completed" ).scalar() or 0 revenue_this_month = db.query(func.coalesce(func.sum(ChargeHistory.amount), 0)).filter( and_( ChargeHistory.status == "completed", func.date(ChargeHistory.created_at) >= this_month_start ) ).scalar() or 0 revenue_last_month = db.query(func.coalesce(func.sum(ChargeHistory.amount), 0)).filter( and_( ChargeHistory.status == "completed", func.date(ChargeHistory.created_at) >= last_month_start, func.date(ChargeHistory.created_at) <= last_month_end ) ).scalar() or 0 # Commission stats from purchased vehicles platform_commission = db.query(func.coalesce(func.sum(PurchasedVehicle.platform_commission), 0)).scalar() or 0 dealer_commission = db.query(func.coalesce(func.sum(PurchasedVehicle.dealer_commission), 0)).scalar() or 0 return RevenueStats( total_revenue=float(total_revenue), revenue_this_month=float(revenue_this_month), revenue_last_month=float(revenue_last_month), platform_commission=float(platform_commission), dealer_commission=float(dealer_commission), ) @router.get("/chart/users", response_model=ChartData) def get_user_chart_data( days: int = 30, db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user) ): """Get user registration chart data for last N days""" today = datetime.utcnow().date() labels = [] values = [] for i in range(days - 1, -1, -1): date = today - timedelta(days=i) count = db.query(func.count(User.id)).filter( and_( User.is_admin == False, func.date(User.created_at) == date ) ).scalar() or 0 labels.append(date.strftime("%m/%d")) values.append(count) return ChartData(labels=labels, values=values) @router.get("/chart/requests", response_model=ChartData) def get_request_chart_data( days: int = 30, db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user) ): """Get vehicle request chart data for last N days""" today = datetime.utcnow().date() labels = [] values = [] for i in range(days - 1, -1, -1): date = today - timedelta(days=i) count = db.query(func.count(VehicleRequest.id)).filter( func.date(VehicleRequest.created_at) == date ).scalar() or 0 labels.append(date.strftime("%m/%d")) values.append(count) return ChartData(labels=labels, values=values) @router.get("/chart/revenue", response_model=ChartData) def get_revenue_chart_data( days: int = 30, db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user) ): """Get daily revenue chart data for last N days""" today = datetime.utcnow().date() labels = [] values = [] for i in range(days - 1, -1, -1): date = today - timedelta(days=i) amount = db.query(func.coalesce(func.sum(ChargeHistory.amount), 0)).filter( and_( ChargeHistory.status == "completed", func.date(ChargeHistory.created_at) == date ) ).scalar() or 0 labels.append(date.strftime("%m/%d")) values.append(int(amount)) return ChartData(labels=labels, values=values) @router.get("/recent-activities", response_model=List[RecentActivity]) def get_recent_activities( limit: int = 10, db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user) ): """Get recent activities across the platform""" activities = [] # Recent user registrations recent_users = db.query(User).filter(User.is_admin == False).order_by( desc(User.created_at) ).limit(3).all() for user in recent_users: activities.append({ "type": "user", "title": "New User Registration", "description": f"{user.name or user.email} joined the platform", "time": user.created_at.isoformat() if user.created_at else "", "icon": "user" }) # Recent vehicle requests recent_requests = db.query(VehicleRequest).order_by( desc(VehicleRequest.created_at) ).limit(3).all() for req in recent_requests: activities.append({ "type": "request", "title": "Vehicle Request", "description": f"Request #{req.id} - {req.status}", "time": req.created_at.isoformat() if req.created_at else "", "icon": "car" }) # Recent inquiries recent_inquiries = db.query(Inquiry).order_by( desc(Inquiry.created_at) ).limit(3).all() for inq in recent_inquiries: activities.append({ "type": "inquiry", "title": "New Inquiry", "description": f"{inq.subject or 'General inquiry'} - {inq.status}", "time": inq.created_at.isoformat() if inq.created_at else "", "icon": "message" }) # Recent dealer applications recent_applications = db.query(DealerApplication).filter( DealerApplication.status == "pending" ).order_by(desc(DealerApplication.applied_at)).limit(2).all() for app in recent_applications: activities.append({ "type": "dealer", "title": "Dealer Application", "description": f"{app.real_name} ({app.business_name}) applied", "time": app.applied_at.isoformat() if app.applied_at else "", "icon": "badge" }) # Recent withdrawals recent_withdrawals = db.query(WithdrawalRequest).filter( WithdrawalRequest.status == "pending" ).order_by(desc(WithdrawalRequest.requested_at)).limit(2).all() for wd in recent_withdrawals: activities.append({ "type": "withdrawal", "title": "Withdrawal Request", "description": f"₩{wd.amount:,.0f} withdrawal requested", "time": wd.requested_at.isoformat() if wd.requested_at else "", "icon": "wallet" }) # Sort by time activities.sort(key=lambda x: x["time"], reverse=True) return [RecentActivity(**a) for a in activities[:limit]] @router.get("/top-dealers", response_model=List[TopDealer]) def get_top_dealers( limit: int = 5, db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user) ): """Get top performing dealers""" # Get dealers with their stats dealers = db.query( DealerInfo, User.name, func.count(PurchasedVehicle.id).label("sales_count"), func.coalesce(func.sum(PurchasedVehicle.dealer_commission), 0).label("total_commission") ).join( User, DealerInfo.user_id == User.id ).outerjoin( PurchasedVehicle, DealerInfo.user_id == PurchasedVehicle.selected_dealer_id ).filter( DealerInfo.is_active == True ).group_by( DealerInfo.id, User.name ).order_by( desc("sales_count") ).limit(limit).all() return [ TopDealer( id=dealer.DealerInfo.id, name=dealer.name or "Unknown", dealer_code=dealer.DealerInfo.dealer_code, total_sales=dealer.sales_count, total_commission=float(dealer.total_commission) ) for dealer in dealers ] @router.get("/pending-actions") def get_pending_actions( db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user) ): """Get counts of pending items requiring admin action""" pending_requests = db.query(func.count(VehicleRequest.id)).filter( VehicleRequest.status == "pending" ).scalar() or 0 pending_inquiries = db.query(func.count(Inquiry.id)).filter( Inquiry.status == InquiryStatus.PENDING ).scalar() or 0 pending_dealer_apps = db.query(func.count(DealerApplication.id)).filter( DealerApplication.status == "pending" ).scalar() or 0 pending_withdrawals = db.query(func.count(WithdrawalRequest.id)).filter( WithdrawalRequest.status == "pending" ).scalar() or 0 return { "pending_requests": pending_requests, "pending_inquiries": pending_inquiries, "pending_dealer_applications": pending_dealer_apps, "pending_withdrawals": pending_withdrawals, "total_pending": pending_requests + pending_inquiries + pending_dealer_apps + pending_withdrawals }