import uuid from typing import Optional from datetime import date, datetime from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.services.admin import AdminService from app.services.translation_quota import TranslationQuotaService from app.services.certification import CertificationService from app.services.invoice import InvoiceService from app.services.payment import PaymentService from app.api.v1.deps import get_current_user router = APIRouter() async def require_admin(current_user: dict = Depends(get_current_user)) -> dict: if current_user.get("role") != "admin": raise HTTPException(status_code=403, detail="Admin access required") return current_user @router.get("/dashboard") async def get_dashboard( _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = AdminService(db) return await service.get_dashboard() @router.get("/users") async def list_users( page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), role: Optional[str] = Query(None), _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = AdminService(db) return await service.list_users(page, size, role) def _validate_uuid(user_id: str): try: uuid.UUID(user_id) except ValueError: raise HTTPException(status_code=400, detail="Invalid user ID format") @router.patch("/users/{target_user_id}/tier") async def update_user_tier( target_user_id: str, data: dict, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): _validate_uuid(target_user_id) service = AdminService(db) tier = data.get("tier") if tier not in ("free", "pro", "enterprise"): raise HTTPException(status_code=400, detail="Invalid tier") success = await service.update_user_tier(target_user_id, tier) if not success: raise HTTPException(status_code=404, detail="User not found") return {"message": f"User tier updated to {tier}"} @router.post("/users/{target_user_id}/toggle-active") async def toggle_user_active( target_user_id: str, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): _validate_uuid(target_user_id) service = AdminService(db) success = await service.toggle_user_active(target_user_id) if not success: raise HTTPException(status_code=404, detail="User not found") return {"message": "User active status toggled"} @router.patch("/users/{target_user_id}/role") async def update_user_role( target_user_id: str, data: dict, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): _validate_uuid(target_user_id) service = AdminService(db) role = data.get("role") if role not in ("user", "admin"): raise HTTPException(status_code=400, detail="Invalid role. Must be 'user' or 'admin'") result = await service.update_user_role(target_user_id, role) if not result: raise HTTPException(status_code=404, detail="User not found") return result @router.get("/users/search") async def search_users( q: str = Query(..., min_length=1), _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = AdminService(db) return await service.search_users(q) @router.get("/users/{target_user_id}") async def get_user_detail( target_user_id: str, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): _validate_uuid(target_user_id) service = AdminService(db) result = await service.get_user_detail(target_user_id) if not result: raise HTTPException(status_code=404, detail="User not found") return result @router.get("/usage-stats") async def get_usage_stats( _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = AdminService(db) return await service.get_usage_stats() @router.get("/logs") async def get_logs( page: int = Query(1, ge=1), size: int = Query(50, ge=1, le=200), action: Optional[str] = Query(None), user_id: Optional[str] = Query(None), date_from: Optional[date] = Query(None), date_to: Optional[date] = Query(None), _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = AdminService(db) dt_from = datetime.combine(date_from, datetime.min.time()) if date_from else None dt_to = datetime.combine(date_to, datetime.max.time()) if date_to else None return await service.get_logs(page, size, action, user_id, dt_from, dt_to) @router.get("/config") async def list_config( _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = AdminService(db) return await service.list_config() @router.put("/config/{key}") async def update_config( key: str, data: dict, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = AdminService(db) item = await service.update_config(key, data.get("value")) if not item: raise HTTPException(status_code=404, detail="Config not found") return item @router.get("/health") async def system_health( db: AsyncSession = Depends(get_db), ): service = AdminService(db) return await service.get_system_health() @router.get("/translation-quotas") async def list_translation_quotas( _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = TranslationQuotaService(db) return await service.get_all_quotas() @router.put("/translation-quotas/{version}") async def update_translation_quota( version: str, data: dict, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): allowed = {"monthly_limit", "enabled", "description"} filtered = {k: v for k, v in data.items() if k in allowed} service = TranslationQuotaService(db) result = await service.update_quota(version, filtered) if not result: raise HTTPException(status_code=404, detail="Quota not found") return result @router.post("/translation-quotas/{version}/reset") async def reset_translation_quota( version: str, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = TranslationQuotaService(db) result = await service.reset_usage(version) if not result: raise HTTPException(status_code=404, detail="Quota not found") return result @router.get("/certifications") async def admin_list_certifications( page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), status: Optional[str] = Query(None), _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = CertificationService(db) return await service.list_all(page, size, status) @router.post("/certifications/{cert_id}/review") async def admin_review_certification( cert_id: str, data: dict, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): _validate_uuid(cert_id) service = CertificationService(db) action = data.get("action") if action not in ("approve", "reject"): raise HTTPException(status_code=400, detail="Action must be 'approve' or 'reject'") result = await service.review(cert_id, action, data.get("reason")) if not result: raise HTTPException(status_code=404, detail="Certification not found") return result @router.get("/invoices") async def admin_list_invoices( page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), status: Optional[str] = Query(None), _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = InvoiceService(db) return await service.list_all(page, size, status) @router.post("/invoices/{invoice_id}/process") async def admin_process_invoice( invoice_id: str, data: dict, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): _validate_uuid(invoice_id) service = InvoiceService(db) action = data.get("action") if action not in ("issue", "reject"): raise HTTPException(status_code=400, detail="Action must be 'issue' or 'reject'") result = await service.process(invoice_id, action, data.get("reason")) if not result: raise HTTPException(status_code=404, detail="Invoice not found") return result @router.get("/payments") async def admin_list_payments( page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), gateway: str = Query(default=""), status: str = Query(default=""), user_id: str = Query(default=""), _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): svc = PaymentService(db) return await svc.admin_list_payments(page, size, gateway, status, user_id) @router.get("/payments/stats") async def admin_payment_stats( _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): svc = PaymentService(db) return await svc.admin_payment_stats() @router.post("/payments/refund") async def admin_refund( data: dict, _: dict = Depends(require_admin), db: AsyncSession = Depends(get_db), ): order_no = data.get("order_no", "") reason = data.get("reason", "") svc = PaymentService(db) try: return await svc.admin_refund(order_no, reason) except ValueError as e: raise HTTPException(status_code=400, detail=str(e))