"""Angle × Concept matrix endpoints.""" from fastapi import APIRouter, HTTPException, Depends from api.schemas import ( MatrixGenerateRequest, MatrixGenerateResponse, MatrixBatchRequest, TestingMatrixResponse, RefineCustomRequest, RefineCustomResponse, ) from services.generator import ad_generator from services.matrix import matrix_service from services.auth_dependency import get_current_user router = APIRouter(tags=["matrix"]) @router.post("/matrix/generate", response_model=MatrixGenerateResponse) async def generate_with_matrix( request: MatrixGenerateRequest, username: str = Depends(get_current_user), ): """ Generate ad using the Angle × Concept matrix approach. Requires authentication. Supports custom angle/concept when key is 'custom'. """ try: return await ad_generator.generate_ad_with_matrix( niche=request.niche, angle_key=request.angle_key, concept_key=request.concept_key, custom_angle=request.custom_angle, custom_concept=request.custom_concept, num_images=request.num_images, image_model=request.image_model, username=username, core_motivator=request.core_motivator, target_audience=request.target_audience, offer=request.offer, ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/matrix/testing", response_model=TestingMatrixResponse) async def generate_testing_matrix(request: MatrixBatchRequest): """ Generate a testing matrix (combinations without images). Strategies: balanced, top_performers, diverse. """ try: combinations = matrix_service.generate_testing_matrix( niche=request.niche, angle_count=request.angle_count, concept_count=request.concept_count, strategy=request.strategy, ) summary = matrix_service.get_matrix_summary(combinations) return { "niche": request.niche, "strategy": request.strategy, "summary": summary, "combinations": combinations, } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/matrix/angles") async def list_angles(): """List all available angles (100 total, 10 categories).""" from data.angles import ANGLES, get_all_angles categories = {} for cat_key, cat_data in ANGLES.items(): categories[cat_key.value] = { "name": cat_data["name"], "angle_count": len(cat_data["angles"]), "angles": [ {"key": a["key"], "name": a["name"], "trigger": a["trigger"], "example": a["example"]} for a in cat_data["angles"] ], } return {"total_angles": len(get_all_angles()), "categories": categories} @router.get("/matrix/concepts") async def list_concepts(): """List all available concepts (100 total, 10 categories).""" from data.concepts import CONCEPTS, get_all_concepts categories = {} for cat_key, cat_data in CONCEPTS.items(): categories[cat_key.value] = { "name": cat_data["name"], "concept_count": len(cat_data["concepts"]), "concepts": [ {"key": c["key"], "name": c["name"], "structure": c["structure"], "visual": c["visual"]} for c in cat_data["concepts"] ], } return {"total_concepts": len(get_all_concepts()), "categories": categories} @router.get("/matrix/angle/{angle_key}") async def get_angle(angle_key: str): """Get details for a specific angle by key.""" from data.angles import get_angle_by_key angle = get_angle_by_key(angle_key) if not angle: raise HTTPException(status_code=404, detail=f"Angle '{angle_key}' not found") return angle @router.get("/matrix/concept/{concept_key}") async def get_concept(concept_key: str): """Get details for a specific concept by key.""" from data.concepts import get_concept_by_key concept = get_concept_by_key(concept_key) if not concept: raise HTTPException(status_code=404, detail=f"Concept '{concept_key}' not found") return concept @router.get("/matrix/compatible/{angle_key}") async def get_compatible_concepts(angle_key: str): """Get concepts compatible with a specific angle.""" from data.angles import get_angle_by_key from data.concepts import get_compatible_concepts as get_compatible angle = get_angle_by_key(angle_key) if not angle: raise HTTPException(status_code=404, detail=f"Angle '{angle_key}' not found") compatible = get_compatible(angle.get("trigger", "")) return { "angle": {"key": angle["key"], "name": angle["name"], "trigger": angle["trigger"]}, "compatible_concepts": [ {"key": c["key"], "name": c["name"], "structure": c["structure"]} for c in compatible ], } @router.post("/matrix/refine-custom", response_model=RefineCustomResponse) async def refine_custom_angle_or_concept(request: RefineCustomRequest): """Refine a custom angle or concept text using AI.""" try: result = await ad_generator.refine_custom_angle_or_concept( text=request.text, type=request.type, niche=request.niche, goal=request.goal, ) return {"status": "success", "type": request.type, "refined": result} except Exception as e: return {"status": "error", "type": request.type, "refined": None, "error": str(e)}