from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from datetime import datetime from typing import List from ..database import get_db from ..models import User, VehicleShare, ShareReward, RequestVehicle from ..models.vehicle_share import generate_share_code from ..schemas import ( VehicleShareCreate, VehicleShareResponse, ShareRewardResponse, ShareRewardSummary, ) from .auth import get_current_user, get_current_user_optional from .notification import notify_share_purchased router = APIRouter(prefix="/share", tags=["vehicle-share"]) # Tax rate for rewards (3.3% withholding tax in Korea) TAX_RATE = 0.033 # Reward percentage (90% of markup goes to sharer) REWARD_RATE = 0.90 @router.post("/create", response_model=VehicleShareResponse) def create_share( share_data: VehicleShareCreate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """Create a shareable link for a vehicle with optional price markup""" # Get the request vehicle request_vehicle = db.query(RequestVehicle).filter( RequestVehicle.id == share_data.request_vehicle_id ).first() if not request_vehicle: raise HTTPException(status_code=404, detail="Vehicle not found") # Check if user owns this request (through VehicleRequest) if request_vehicle.vehicle_request.user_id != current_user.id: raise HTTPException(status_code=403, detail="You can only share vehicles from your own requests") # Check if vehicle is approved if not request_vehicle.is_approved: raise HTTPException(status_code=400, detail="Only approved vehicles can be shared") # Generate unique share code share_code = generate_share_code() while db.query(VehicleShare).filter(VehicleShare.share_code == share_code).first(): share_code = generate_share_code() # Calculate prices original_price = request_vehicle.price_krw or 0 markup = share_data.markup_amount_krw if share_data.markup_amount_krw > 0 else 0 shared_price = original_price + markup # Create share vehicle_share = VehicleShare( user_id=current_user.id, request_vehicle_id=share_data.request_vehicle_id, share_code=share_code, original_price_krw=original_price, markup_amount_krw=markup, shared_price_krw=shared_price, ) db.add(vehicle_share) db.commit() db.refresh(vehicle_share) return vehicle_share @router.get("/my-shares", response_model=List[VehicleShareResponse]) def get_my_shares( current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """Get all vehicle shares created by current user""" shares = db.query(VehicleShare).filter( VehicleShare.user_id == current_user.id ).order_by(VehicleShare.created_at.desc()).all() return shares @router.get("/my-rewards", response_model=List[ShareRewardResponse]) def get_my_rewards( current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """Get all rewards earned from vehicle shares""" rewards = db.query(ShareReward).filter( ShareReward.user_id == current_user.id ).order_by(ShareReward.created_at.desc()).all() return rewards @router.get("/my-rewards/summary", response_model=ShareRewardSummary) def get_rewards_summary( current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """Get summary of share rewards""" rewards = db.query(ShareReward).filter( ShareReward.user_id == current_user.id ).all() total_rewards = sum(r.net_amount for r in rewards) total_withdrawn = sum(r.net_amount for r in rewards if r.status == "withdrawn") pending = sum(r.net_amount for r in rewards if r.status == "pending") approved = sum(r.net_amount for r in rewards if r.status == "approved") return ShareRewardSummary( total_rewards=total_rewards, total_withdrawn=total_withdrawn, pending_amount=pending, available_for_withdrawal=approved, reward_count=len(rewards) ) @router.get("/{share_code}") def get_shared_vehicle( share_code: str, current_user: User = Depends(get_current_user_optional), db: Session = Depends(get_db) ): """Get shared vehicle details (public endpoint)""" share = db.query(VehicleShare).filter( VehicleShare.share_code == share_code ).first() if not share: raise HTTPException(status_code=404, detail="Shared vehicle not found") # Increment view count share.view_count += 1 db.commit() # Get vehicle details vehicle = share.request_vehicle return { "share": { "id": share.id, "share_code": share.share_code, "shared_price_krw": share.shared_price_krw, "original_price_krw": share.original_price_krw, "markup_amount_krw": share.markup_amount_krw, "view_count": share.view_count, "is_purchased": share.is_purchased, "created_at": share.created_at, }, "vehicle": { "id": vehicle.id, "car_id": vehicle.car_id, "maker": vehicle.maker, "model": vehicle.model, "year": vehicle.year, "mileage": vehicle.mileage, "fuel_type": vehicle.fuel_type, "color": vehicle.color, "grade": vehicle.grade, "image_url": vehicle.image_url, "performance_check_url": vehicle.performance_check_url, "dealer_name": vehicle.dealer_name, "dealer_phone": vehicle.dealer_phone, }, "sharer": { "name": share.user.name or "Anonymous", } } @router.post("/{share_code}/purchase") def purchase_shared_vehicle( share_code: str, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """Purchase a vehicle through a shared link""" share = db.query(VehicleShare).filter( VehicleShare.share_code == share_code ).first() if not share: raise HTTPException(status_code=404, detail="Shared vehicle not found") if share.is_purchased: raise HTTPException(status_code=400, detail="This vehicle has already been purchased") if share.user_id == current_user.id: raise HTTPException(status_code=400, detail="You cannot purchase your own shared vehicle") # Mark as purchased share.is_purchased = True share.purchased_by_user_id = current_user.id share.purchased_at = datetime.utcnow() # Create reward for the sharer (if there's markup) reward_net = 0 if share.markup_amount_krw > 0: reward_amount = share.markup_amount_krw * REWARD_RATE # 90% tax_amount = reward_amount * TAX_RATE # 3.3% tax net_amount = reward_amount - tax_amount reward_net = net_amount reward = ShareReward( user_id=share.user_id, vehicle_share_id=share.id, markup_amount=share.markup_amount_krw, reward_amount=reward_amount, tax_amount=tax_amount, net_amount=net_amount, status="pending" # Needs admin approval ) db.add(reward) db.commit() # Send notification to sharer about the sale vehicle = share.request_vehicle car_name = f"{vehicle.maker} {vehicle.model}" if vehicle else "차량" notify_share_purchased(db, share.user_id, share.id, reward_net, car_name) return { "message": "Vehicle purchase initiated", "share_code": share_code, "price": share.shared_price_krw } # Admin endpoints @router.get("/admin/all", response_model=List[VehicleShareResponse]) def get_all_shares( current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """[Admin] Get all vehicle shares""" if not current_user.is_admin: raise HTTPException(status_code=403, detail="Admin access required") shares = db.query(VehicleShare).order_by(VehicleShare.created_at.desc()).all() return shares @router.get("/admin/rewards", response_model=List[ShareRewardResponse]) def get_all_rewards( status_filter: str = None, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """[Admin] Get all share rewards""" if not current_user.is_admin: raise HTTPException(status_code=403, detail="Admin access required") query = db.query(ShareReward) if status_filter: query = query.filter(ShareReward.status == status_filter) rewards = query.order_by(ShareReward.created_at.desc()).all() return rewards @router.put("/admin/rewards/{reward_id}/approve") def approve_reward( reward_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """[Admin] Approve a share reward for withdrawal""" if not current_user.is_admin: raise HTTPException(status_code=403, detail="Admin access required") reward = db.query(ShareReward).filter(ShareReward.id == reward_id).first() if not reward: raise HTTPException(status_code=404, detail="Reward not found") if reward.status != "pending": raise HTTPException(status_code=400, detail="Reward is not pending") reward.status = "approved" db.commit() return {"message": "Reward approved", "reward_id": reward_id}