from typing import Any, Optional from fastapi import APIRouter, Depends, Header, Request from pydantic import BaseModel, Field from sqlalchemy.orm import Session from app.core.response import success_response from app.db import get_db_session from app.services.audit_log_service import AuditLogService from app.services.auth_service import AuthService from app.services.clause_recommendation_service import ClauseRecommendationService router = APIRouter(prefix="/clauses", tags=["clauses"]) ROUTE_PERMISSION_SCOPE = "clause_recommendation" ROUTE_PERMISSION_ACTIONS = { "recommend": "clauses:recommend", "rewrite": "clauses:rewrite", "recommendation_action": "clauses:recommendations:action", } class ClauseRecommendRequest(BaseModel): contract_text: str document_type_code: Optional[str] = None contract_type: Optional[str] = None contract_id: Optional[str] = None max_items: int = Field(default=8, ge=1, le=20) class ClauseRewriteRequest(BaseModel): clause_text: str rewrite_type: str = Field(default="professional") document_type_code: Optional[str] = None contract_type: Optional[str] = None contract_id: Optional[str] = None clause_id: Optional[str] = None context_text: Optional[str] = None class ClauseRecommendationActionRequest(BaseModel): action_status: str = Field(description="shown/applied/ignored/modified") action_note: Optional[str] = None applied_contract_version_id: Optional[str] = None action_detail: Optional[dict[str, Any]] = None def get_clause_recommendation_service(db: Session = Depends(get_db_session)) -> ClauseRecommendationService: return ClauseRecommendationService(db) def get_audit_log_service(db: Session = Depends(get_db_session)) -> AuditLogService: return AuditLogService(db) def require_current_user( authorization: Optional[str] = Header(default=None), db: Session = Depends(get_db_session), ): return AuthService(db).require_current_user(authorization) @router.post("/recommend") def recommend_missing_clauses( payload: ClauseRecommendRequest, request: Request, clause_recommendation_service: ClauseRecommendationService = Depends(get_clause_recommendation_service), audit_log_service: AuditLogService = Depends(get_audit_log_service), current_user: dict = Depends(require_current_user), ): result = clause_recommendation_service.recommend_missing_clauses( current_user, contract_text=payload.contract_text, document_type_code=payload.document_type_code, contract_type=payload.contract_type, contract_id=payload.contract_id, max_items=payload.max_items, ) audit_log_service.record_event( tenant_id=current_user.get("tenant_id"), user_id=current_user.get("id"), action="recommend_missing_clauses", resource_type="clause_recommendation", resource_id=payload.contract_id, detail_json={ "document_type_code": result.get("document_type_code"), "contract_type": payload.contract_type, "recommended_count": result.get("total"), "recommendation_keys": (result.get("summary") or {}).get("recommendation_keys") or [], "missing_clause_keys": (result.get("summary") or {}).get("missing_clause_keys") or [], "industry_practice_keys": (result.get("summary") or {}).get("industry_practice_keys") or [], "industry_practice_count": (result.get("summary") or {}).get("industry_practice_count") or 0, "source_count": (result.get("summary") or {}).get("source_count") or 0, "text_length": len(payload.contract_text or ""), }, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent"), ) return success_response(result) @router.post("/rewrite") def rewrite_clause_wording( payload: ClauseRewriteRequest, request: Request, clause_recommendation_service: ClauseRecommendationService = Depends(get_clause_recommendation_service), audit_log_service: AuditLogService = Depends(get_audit_log_service), current_user: dict = Depends(require_current_user), ): result = clause_recommendation_service.rewrite_clause_wording( current_user, clause_text=payload.clause_text, rewrite_type=payload.rewrite_type, document_type_code=payload.document_type_code, contract_type=payload.contract_type, contract_id=payload.contract_id, clause_id=payload.clause_id, context_text=payload.context_text, ) audit_log_service.record_event( tenant_id=current_user.get("tenant_id"), user_id=current_user.get("id"), action="rewrite_clause_wording", resource_type="clause_rewrite", resource_id=payload.clause_id or payload.contract_id, detail_json={ "contract_id": payload.contract_id, "clause_id": payload.clause_id, "document_type_code": result.get("document_type_code"), "rewrite_type": result.get("rewrite_type"), "change_count": (result.get("summary") or {}).get("change_count") or 0, "risk_note_count": (result.get("summary") or {}).get("risk_note_count") or 0, "source_count": (result.get("summary") or {}).get("source_count") or 0, "text_length": len(payload.clause_text or ""), }, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent"), ) return success_response(result) @router.post("/recommendations/{recommendation_id}/action") def record_clause_recommendation_action( recommendation_id: str, payload: ClauseRecommendationActionRequest, request: Request, clause_recommendation_service: ClauseRecommendationService = Depends(get_clause_recommendation_service), audit_log_service: AuditLogService = Depends(get_audit_log_service), current_user: dict = Depends(require_current_user), ): result = clause_recommendation_service.update_recommendation_action( current_user, recommendation_id, action_status=payload.action_status, action_note=payload.action_note, applied_contract_version_id=payload.applied_contract_version_id, action_detail=payload.action_detail, ) audit_log_service.record_event( tenant_id=current_user.get("tenant_id"), user_id=current_user.get("id"), action="record_clause_recommendation_action", resource_type="clause_recommendation", resource_id=recommendation_id, detail_json={ "action_status": result.get("action_status"), "contract_id": result.get("contract_id"), "document_type_code": result.get("document_type_code"), "clause_key": result.get("clause_key"), "recommendation_kind": result.get("recommendation_kind"), "applied_contract_version_id": result.get("applied_contract_version_id"), }, ip_address=request.client.host if request.client else None, user_agent=request.headers.get("user-agent"), ) return success_response(result)