""" API Routes for Thread Series Management. """ from datetime import datetime from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from pydantic import BaseModel from app.core.database import get_db from app.services.thread_service import thread_service router = APIRouter() class ThreadPostCreate(BaseModel): """Schema for a post in a thread.""" content: str image_url: Optional[str] = None class ThreadSeriesCreate(BaseModel): """Schema for creating a thread series.""" name: str platform: str posts: List[ThreadPostCreate] description: Optional[str] = None topic: Optional[str] = None schedule_type: str = "sequential" interval_minutes: int = 5 hashtags: Optional[List[str]] = None class ThreadGenerateRequest(BaseModel): """Schema for AI thread generation.""" topic: str platform: str num_posts: int = 5 style: str = "educational" name: Optional[str] = None class ScheduleRequest(BaseModel): """Schema for scheduling a series.""" start_time: Optional[datetime] = None @router.get("/") async def list_thread_series( status: Optional[str] = None, platform: Optional[str] = None, limit: int = Query(20, ge=1, le=100), db: Session = Depends(get_db) ): """ List all thread series. - **status**: Filter by status (draft, scheduled, publishing, completed) - **platform**: Filter by platform """ series_list = await thread_service.get_series_list( status=status, platform=platform, limit=limit ) return {"series": series_list, "count": len(series_list)} @router.get("/{series_id}") async def get_thread_series( series_id: int, db: Session = Depends(get_db) ): """ Get a specific thread series with all its posts. """ series = await thread_service.get_series(series_id) if not series: raise HTTPException(status_code=404, detail="Series not found") return series @router.post("/") async def create_thread_series( series_data: ThreadSeriesCreate, db: Session = Depends(get_db) ): """ Create a new thread series manually. - **name**: Series name - **platform**: Target platform (x, threads) - **posts**: List of posts with content and optional image_url - **schedule_type**: "sequential" (posts one after another) or "timed" - **interval_minutes**: Minutes between posts (default 5) """ if len(series_data.posts) < 2: raise HTTPException( status_code=400, detail="Thread series requires at least 2 posts" ) if len(series_data.posts) > 20: raise HTTPException( status_code=400, detail="Maximum 20 posts per thread series" ) try: series = await thread_service.create_series( name=series_data.name, platform=series_data.platform, posts_content=[p.dict() for p in series_data.posts], description=series_data.description, topic=series_data.topic, schedule_type=series_data.schedule_type, interval_minutes=series_data.interval_minutes, hashtags=series_data.hashtags ) return { "message": "Thread series created successfully", "series": series.to_dict() } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/generate") async def generate_thread_with_ai( gen_data: ThreadGenerateRequest, db: Session = Depends(get_db) ): """ Generate a thread series using AI. - **topic**: The topic to generate content about - **platform**: Target platform - **num_posts**: Number of posts (2-10) - **style**: Content style (educational, storytelling, tips) """ if gen_data.num_posts < 2 or gen_data.num_posts > 10: raise HTTPException( status_code=400, detail="Number of posts must be between 2 and 10" ) result = await thread_service.generate_thread_with_ai( topic=gen_data.topic, platform=gen_data.platform, num_posts=gen_data.num_posts, style=gen_data.style ) if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("error", "AI generation failed") ) # If a name was provided, also create the series if gen_data.name and result.get("posts"): posts_content = [{"content": p["content"]} for p in result["posts"]] series = await thread_service.create_series( name=gen_data.name, platform=gen_data.platform, posts_content=posts_content, topic=gen_data.topic, ai_generated=True, generation_prompt=f"Topic: {gen_data.topic}, Style: {gen_data.style}" ) return { "message": "Thread generated and saved", "series": series.to_dict(), "generated_posts": result["posts"] } return { "message": "Thread generated (not saved)", "generated_posts": result["posts"], "count": result["count"] } @router.post("/{series_id}/schedule") async def schedule_series( series_id: int, schedule_data: ScheduleRequest = None, db: Session = Depends(get_db) ): """ Schedule a thread series for publishing. - **start_time**: When to start publishing (optional, defaults to now + 5 min) """ start_time = schedule_data.start_time if schedule_data else None result = await thread_service.schedule_series( series_id=series_id, start_time=start_time ) if not result.get("success"): raise HTTPException( status_code=400, detail=result.get("error", "Failed to schedule") ) return { "message": "Series scheduled successfully", **result } @router.post("/{series_id}/publish-next") async def publish_next_post( series_id: int, db: Session = Depends(get_db) ): """ Manually trigger publishing the next post in a series. """ result = await thread_service.publish_next_post(series_id) if not result.get("success"): raise HTTPException( status_code=400, detail=result.get("error", "Failed to publish") ) return result @router.post("/{series_id}/cancel") async def cancel_series( series_id: int, db: Session = Depends(get_db) ): """ Cancel a thread series and its scheduled posts. """ result = await thread_service.cancel_series(series_id) if not result.get("success"): raise HTTPException( status_code=400, detail=result.get("error", "Failed to cancel") ) return {"message": "Series cancelled successfully"}