from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile, File, Response from sqlalchemy.ext.asyncio import AsyncSession from typing import Optional, List from app.database import get_db from app.services.customer import CustomerService from app.services.customer_health import CustomerHealthService from app.services.import_service import import_service from app.services.usage import UsageService from app.services import export from app.core.security import decode_token from app.api.v1.deps import get_current_user_id router = APIRouter() @router.get("") async def list_customers( status: Optional[str] = None, page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=1000), user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerService(db) return await service.list_customers(user_id, status, page, size) @router.get("/silent") async def get_silent( days: int = Query(3, ge=1), user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerService(db) customers = await service.get_silent_customers(user_id, days) return { "customers": customers, "count": len(customers), "silence_days": days, } @router.get("/health-overview") async def get_health_overview( user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerHealthService(db) return await service.get_health_overview(user_id) @router.get("/health-scores") async def get_all_health_scores( user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerHealthService(db) return {"items": await service.get_all_health_scores(user_id)} @router.get("/{customer_id}/health") async def get_customer_health( customer_id: str, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerHealthService(db) return await service.get_customer_health(user_id, customer_id) @router.get("/{customer_id}/conversation") async def get_conversation( customer_id: str, page: int = 1, size: int = 20, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerService(db) return await service.get_conversation(user_id, customer_id, page, size) @router.get("/{customer_id}") async def get_customer( customer_id: str, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerService(db) customer = await service.get_customer(user_id, customer_id) if not customer: raise HTTPException(status_code=404, detail="Customer not found") return customer @router.post("") async def create_customer( data: dict, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): usage = UsageService(db) ok, msg = await usage.check_quota(user_id, "create_customer") if not ok: raise HTTPException(status_code=429, detail=msg) service = CustomerService(db) customer = await service.create_customer(user_id, data) await usage.record_usage(user_id, "create_customer") return customer @router.patch("/{customer_id}") async def update_customer( customer_id: str, data: dict, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerService(db) customer = await service.update_customer(user_id, customer_id, data) if not customer: raise HTTPException(status_code=404, detail="Customer not found") return customer @router.delete("/{customer_id}") async def delete_customer( customer_id: str, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerService(db) deleted = await service.delete_customer(user_id, customer_id) if not deleted: raise HTTPException(status_code=404, detail="Customer not found") return {"message": "Customer deleted"} MAX_UPLOAD_SIZE = 10 * 1024 * 1024 # 10MB @router.post("/import") async def import_customers( file: UploadFile = File(...), user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): from app.workers.tasks import process_customer_import filename = file.filename or "unknown" file_size = 0 content = b"" while True: chunk = await file.read(8192) if not chunk: break file_size += len(chunk) if file_size > MAX_UPLOAD_SIZE: raise HTTPException(status_code=413, detail=f"File too large. Max {MAX_UPLOAD_SIZE // (1024*1024)}MB") content += chunk if filename.endswith(".xlsx"): records, parse_errors = import_service.parse_xlsx(content) elif filename.endswith(".csv"): records, parse_errors = import_service.parse_csv(content) else: raise HTTPException(status_code=400, detail="Unsupported file format. Use .xlsx or .csv") if parse_errors and not records: raise HTTPException(status_code=400, detail="Parse failed. Check file format.") valid, validation_errors = import_service.validate_records(records) all_errors = parse_errors + validation_errors imported_count = 0 for record in valid: try: svc = CustomerService(db) await svc.create_customer(user_id, record) imported_count += 1 except Exception as e: all_errors.append(f"Import failed for row: {str(e)}") return { "imported": imported_count, "total": len(records), "errors": all_errors, "filename": filename, } @router.get("/export/csv") async def export_customers( status: Optional[str] = None, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerService(db) result = await service.list_customers(user_id, status, 1, 9999) items = result.get("items", []) csv_bytes = export.export_customers_csv(items) return Response( content=csv_bytes, media_type="text/csv", headers={"Content-Disposition": "attachment; filename=customers.csv"}, ) @router.get("/export/xlsx") async def export_customers_xlsx( status: Optional[str] = None, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = CustomerService(db) result = await service.list_customers(user_id, status, 1, 9999) items = result.get("items", []) xlsx_bytes = export.export_customers_xlsx(items) return Response( content=xlsx_bytes, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", headers={"Content-Disposition": "attachment; filename=customers.xlsx"}, )