""" Email Management Router API endpoints for email viewing, classification, and rule management """ import logging from fastapi import APIRouter, HTTPException, Query from typing import List, Optional from pydantic import BaseModel from datetime import datetime, date from app.core.database import execute_query, execute_insert from app.services.email_processor_service import EmailProcessorService logger = logging.getLogger(__name__) router = APIRouter() # Pydantic Models class EmailListItem(BaseModel): id: int message_id: str subject: str sender_email: str sender_name: Optional[str] received_date: datetime classification: Optional[str] confidence_score: Optional[float] status: str is_read: bool has_attachments: bool attachment_count: int rule_name: Optional[str] = None supplier_name: Optional[str] = None customer_name: Optional[str] = None class EmailDetail(BaseModel): id: int message_id: str subject: str sender_email: str sender_name: Optional[str] recipient_email: Optional[str] cc: Optional[str] body_text: Optional[str] body_html: Optional[str] received_date: datetime folder: str classification: Optional[str] confidence_score: Optional[float] status: str is_read: bool has_attachments: bool attachment_count: int rule_id: Optional[int] supplier_id: Optional[int] customer_id: Optional[int] linked_case_id: Optional[int] extracted_invoice_number: Optional[str] extracted_amount: Optional[float] extracted_due_date: Optional[date] auto_processed: bool created_at: datetime updated_at: datetime class EmailRule(BaseModel): id: Optional[int] = None name: str description: Optional[str] conditions: dict action_type: str action_params: Optional[dict] = {} priority: int = 100 enabled: bool = True match_count: int = 0 last_matched_at: Optional[datetime] class ProcessingStats(BaseModel): status: str fetched: int = 0 saved: int = 0 classified: int = 0 rules_matched: int = 0 errors: int = 0 # Email Endpoints @router.get("/emails", response_model=List[EmailListItem]) async def list_emails( status: Optional[str] = Query(None), classification: Optional[str] = Query(None), limit: int = Query(50, le=500), offset: int = Query(0, ge=0) ): """Get list of emails with filtering""" try: where_clauses = ["em.deleted_at IS NULL"] params = [] if status: where_clauses.append("em.status = %s") params.append(status) if classification: where_clauses.append("em.classification = %s") params.append(classification) where_sql = " AND ".join(where_clauses) query = f""" SELECT em.id, em.message_id, em.subject, em.sender_email, em.sender_name, em.received_date, em.classification, em.confidence_score, em.status, em.is_read, em.has_attachments, em.attachment_count, er.name as rule_name, v.name as supplier_name, NULL as customer_name FROM email_messages em LEFT JOIN email_rules er ON em.rule_id = er.id LEFT JOIN vendors v ON em.supplier_id = v.id WHERE {where_sql} ORDER BY em.received_date DESC LIMIT %s OFFSET %s """ params.extend([limit, offset]) result = execute_query(query, tuple(params)) return result except Exception as e: logger.error(f"❌ Error listing emails: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/emails/{email_id}", response_model=EmailDetail) async def get_email(email_id: int): """Get email detail by ID""" try: query = """ SELECT * FROM email_messages WHERE id = %s AND deleted_at IS NULL """ result = execute_query(query, (email_id,)) if not result: raise HTTPException(status_code=404, detail="Email not found") # Mark as read update_query = "UPDATE email_messages SET is_read = true WHERE id = %s" execute_query(update_query, (email_id,)) return result[0] except HTTPException: raise except Exception as e: logger.error(f"❌ Error getting email {email_id}: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.post("/emails/process") async def process_emails(): """Manually trigger email processing""" try: processor = EmailProcessorService() stats = await processor.process_inbox() return { "success": True, "message": "Email processing completed", "stats": stats } except Exception as e: logger.error(f"❌ Email processing failed: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.post("/emails/{email_id}/reprocess") async def reprocess_email(email_id: int): """Manually reprocess a single email (reclassify + rematch rules)""" try: processor = EmailProcessorService() await processor.reprocess_email(email_id) return { "success": True, "message": f"Email {email_id} reprocessed successfully" } except Exception as e: logger.error(f"❌ Error reprocessing email {email_id}: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.put("/emails/{email_id}/classify") async def update_classification(email_id: int, classification: str): """Manually update email classification""" try: valid_classifications = [ 'invoice', 'freight_note', 'order_confirmation', 'time_confirmation', 'case_notification', 'customer_email', 'bankruptcy', 'general', 'spam', 'unknown' ] if classification not in valid_classifications: raise HTTPException(status_code=400, detail=f"Invalid classification. Must be one of: {valid_classifications}") query = """ UPDATE email_messages SET classification = %s, classification_date = CURRENT_TIMESTAMP WHERE id = %s AND deleted_at IS NULL """ execute_query(query, (classification, email_id)) return { "success": True, "message": f"Email {email_id} classified as '{classification}'" } except HTTPException: raise except Exception as e: logger.error(f"❌ Error updating classification: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.delete("/emails/{email_id}") async def delete_email(email_id: int): """Soft delete email""" try: query = """ UPDATE email_messages SET deleted_at = CURRENT_TIMESTAMP WHERE id = %s """ execute_query(query, (email_id,)) return { "success": True, "message": f"Email {email_id} deleted" } except Exception as e: logger.error(f"❌ Error deleting email: {e}") raise HTTPException(status_code=500, detail=str(e)) # Email Rules Endpoints @router.get("/email-rules", response_model=List[EmailRule]) async def list_rules(): """Get all email rules""" try: query = """ SELECT * FROM email_rules ORDER BY priority ASC, name ASC """ result = execute_query(query) return result except Exception as e: logger.error(f"❌ Error listing rules: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.post("/email-rules", response_model=EmailRule) async def create_rule(rule: EmailRule): """Create new email rule""" try: query = """ INSERT INTO email_rules (name, description, conditions, action_type, action_params, priority, enabled, created_by_user_id) VALUES (%s, %s, %s, %s, %s, %s, %s, 1) RETURNING * """ import json result = execute_query(query, ( rule.name, rule.description, json.dumps(rule.conditions), rule.action_type, json.dumps(rule.action_params or {}), rule.priority, rule.enabled )) if result: logger.info(f"✅ Created email rule: {rule.name}") return result[0] else: raise HTTPException(status_code=500, detail="Failed to create rule") except Exception as e: logger.error(f"❌ Error creating rule: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.put("/email-rules/{rule_id}", response_model=EmailRule) async def update_rule(rule_id: int, rule: EmailRule): """Update existing email rule""" try: import json query = """ UPDATE email_rules SET name = %s, description = %s, conditions = %s, action_type = %s, action_params = %s, priority = %s, enabled = %s WHERE id = %s RETURNING * """ result = execute_query(query, ( rule.name, rule.description, json.dumps(rule.conditions), rule.action_type, json.dumps(rule.action_params or {}), rule.priority, rule.enabled, rule_id )) if result: logger.info(f"✅ Updated email rule {rule_id}") return result[0] else: raise HTTPException(status_code=404, detail="Rule not found") except Exception as e: logger.error(f"❌ Error updating rule: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.delete("/email-rules/{rule_id}") async def delete_rule(rule_id: int): """Delete email rule""" try: query = "DELETE FROM email_rules WHERE id = %s" execute_query(query, (rule_id,)) return { "success": True, "message": f"Rule {rule_id} deleted" } except Exception as e: logger.error(f"❌ Error deleting rule: {e}") raise HTTPException(status_code=500, detail=str(e)) # Statistics Endpoint @router.get("/emails/stats/summary") async def get_email_stats(): """Get email processing statistics""" try: query = """ SELECT COUNT(*) as total_emails, COUNT(CASE WHEN status = 'new' THEN 1 END) as new_emails, COUNT(CASE WHEN status = 'processed' THEN 1 END) as processed_emails, COUNT(CASE WHEN classification = 'invoice' THEN 1 END) as invoices, COUNT(CASE WHEN classification = 'time_confirmation' THEN 1 END) as time_confirmations, COUNT(CASE WHEN classification = 'spam' THEN 1 END) as spam_emails, COUNT(CASE WHEN auto_processed THEN 1 END) as auto_processed, AVG(confidence_score) as avg_confidence FROM email_messages WHERE deleted_at IS NULL """ result = execute_query(query) return result[0] if result else {} except Exception as e: logger.error(f"❌ Error getting stats: {e}") raise HTTPException(status_code=500, detail=str(e))