"""Support ticket persistence and message operations.""" from database_service import database_connection, dict_from_row from runtime_config import ( logger, ) from typing import Dict, List, Optional import uuid __all__ = ['add_ticket_attachment', 'add_ticket_message', 'create_ticket', 'get_all_tickets', 'get_ticket', 'get_ticket_attachments', 'get_ticket_stats', 'get_user_tickets', 'update_ticket_status'] def create_ticket(user_id: str, subject: str, description: str, priority: str = 'medium', category: str = 'general') -> Optional[Dict]: """Create a new support ticket""" try: ticket_id = f"TKT-{str(uuid.uuid4())[:8]}" with database_connection() as conn: cursor = conn.cursor() cursor.execute(''' INSERT INTO tickets (id, user_id, subject, description, priority, category) VALUES (?, ?, ?, ?, ?, ?) ''', (ticket_id, user_id, subject, description, priority, category)) conn.commit() cursor.execute(''' SELECT t.*, u.username as user_username FROM tickets t JOIN users u ON t.user_id = u.id WHERE t.id = ? ''', (ticket_id,)) ticket = dict_from_row(cursor.fetchone()) return ticket except Exception as e: logger.error(f"Ticket creation failed: {e}") return None def get_ticket(ticket_id: str) -> Optional[Dict]: """Get ticket by ID with user information""" try: with database_connection() as conn: cursor = conn.cursor() cursor.execute(''' SELECT t.*, u.username as user_username FROM tickets t JOIN users u ON t.user_id = u.id WHERE t.id = ? ''', (ticket_id,)) ticket = dict_from_row(cursor.fetchone()) if ticket: # Get ticket messages cursor.execute(''' SELECT tm.*, u.username as user_username FROM ticket_messages tm JOIN users u ON tm.user_id = u.id WHERE tm.ticket_id = ? ORDER BY tm.created_at ASC ''', (ticket_id,)) messages = [dict_from_row(row) for row in cursor.fetchall()] ticket['messages'] = messages # Get message count cursor.execute('SELECT COUNT(*) as count FROM ticket_messages WHERE ticket_id = ?', (ticket_id,)) ticket['message_count'] = cursor.fetchone()['count'] # Get last reply time cursor.execute(''' SELECT MAX(created_at) as last_reply FROM ticket_messages WHERE ticket_id = ? ''', (ticket_id,)) last_reply = cursor.fetchone()['last_reply'] ticket['last_reply'] = last_reply return ticket except Exception as e: logger.error(f"Error getting ticket: {e}") return None def get_user_tickets(user_id: str, include_closed: bool = False) -> List[Dict]: """Get all tickets for a user""" try: with database_connection() as conn: cursor = conn.cursor() if include_closed: cursor.execute(''' SELECT t.*, u.username as user_username FROM tickets t JOIN users u ON t.user_id = u.id WHERE t.user_id = ? ORDER BY t.updated_at DESC ''', (user_id,)) else: cursor.execute(''' SELECT t.*, u.username as user_username FROM tickets t JOIN users u ON t.user_id = u.id WHERE t.user_id = ? AND t.status != 'closed' ORDER BY t.updated_at DESC ''', (user_id,)) tickets = [dict_from_row(row) for row in cursor.fetchall()] # Add message counts and last reply for ticket in tickets: cursor.execute('SELECT COUNT(*) as count FROM ticket_messages WHERE ticket_id = ?', (ticket['id'],)) ticket['message_count'] = cursor.fetchone()['count'] cursor.execute(''' SELECT MAX(created_at) as last_reply FROM ticket_messages WHERE ticket_id = ? ''', (ticket['id'],)) last_reply = cursor.fetchone()['last_reply'] ticket['last_reply'] = last_reply return tickets except Exception as e: logger.error(f"Error getting user tickets: {e}") return [] def get_all_tickets(status_filter: str = None, priority_filter: str = None) -> List[Dict]: """Get all tickets (admin only)""" try: with database_connection() as conn: cursor = conn.cursor() query = ''' SELECT t.*, u.username as user_username FROM tickets t JOIN users u ON t.user_id = u.id ''' params = [] conditions = [] if status_filter and status_filter != 'all': conditions.append('t.status = ?') params.append(status_filter) if priority_filter and priority_filter != 'all': conditions.append('t.priority = ?') params.append(priority_filter) if conditions: query += ' WHERE ' + ' AND '.join(conditions) query += ' ORDER BY t.updated_at DESC' cursor.execute(query, params) tickets = [dict_from_row(row) for row in cursor.fetchall()] # Add message counts and last reply for ticket in tickets: cursor.execute('SELECT COUNT(*) as count FROM ticket_messages WHERE ticket_id = ?', (ticket['id'],)) ticket['message_count'] = cursor.fetchone()['count'] cursor.execute(''' SELECT MAX(created_at) as last_reply FROM ticket_messages WHERE ticket_id = ? ''', (ticket['id'],)) last_reply = cursor.fetchone()['last_reply'] ticket['last_reply'] = last_reply return tickets except Exception as e: logger.error(f"Error getting all tickets: {e}") return [] def add_ticket_message(ticket_id: str, user_id: str, message: str, is_admin: bool = False) -> Optional[Dict]: """Add a message to a ticket""" try: message_id = f"MSG-{str(uuid.uuid4())[:8]}" with database_connection() as conn: cursor = conn.cursor() # Add message cursor.execute(''' INSERT INTO ticket_messages (id, ticket_id, user_id, message, is_admin) VALUES (?, ?, ?, ?, ?) ''', (message_id, ticket_id, user_id, message, is_admin)) # Update ticket timestamp cursor.execute(''' UPDATE tickets SET updated_at = CURRENT_TIMESTAMP WHERE id = ? ''', (ticket_id,)) conn.commit() # Get the created message with user info cursor.execute(''' SELECT tm.*, u.username as user_username FROM ticket_messages tm JOIN users u ON tm.user_id = u.id WHERE tm.id = ? ''', (message_id,)) message_data = dict_from_row(cursor.fetchone()) return message_data except Exception as e: logger.error(f"Error adding ticket message: {e}") return None def update_ticket_status(ticket_id: str, status: str, assigned_admin_id: str = None) -> bool: """Update ticket status""" try: with database_connection() as conn: cursor = conn.cursor() if assigned_admin_id: cursor.execute(''' UPDATE tickets SET status = ?, assigned_admin_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? ''', (status, assigned_admin_id, ticket_id)) else: cursor.execute(''' UPDATE tickets SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? ''', (status, ticket_id)) conn.commit() return cursor.rowcount > 0 except Exception as e: logger.error(f"Error updating ticket status: {e}") return False def add_ticket_attachment(ticket_id: str, message_id: str, filename: str, file_data: bytes, mime_type: str, uploaded_by: str) -> Optional[Dict]: """Add attachment to ticket""" try: attachment_id = f"ATT-{str(uuid.uuid4())[:8]}" file_size = len(file_data) with database_connection() as conn: cursor = conn.cursor() cursor.execute(''' INSERT INTO ticket_attachments (id, ticket_id, message_id, filename, file_data, mime_type, file_size, uploaded_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ''', (attachment_id, ticket_id, message_id, filename, file_data, mime_type, file_size, uploaded_by)) conn.commit() cursor.execute('SELECT * FROM ticket_attachments WHERE id = ?', (attachment_id,)) return dict_from_row(cursor.fetchone()) except Exception as e: logger.error(f"Error adding ticket attachment: {e}") return None def get_ticket_attachments(ticket_id: str) -> List[Dict]: """Get all attachments for a ticket""" try: with database_connection() as conn: cursor = conn.cursor() cursor.execute(''' SELECT * FROM ticket_attachments WHERE ticket_id = ? ORDER BY created_at DESC ''', (ticket_id,)) return [dict_from_row(row) for row in cursor.fetchall()] except Exception as e: logger.error(f"Error getting ticket attachments: {e}") return [] def get_ticket_stats() -> Dict: """Get ticket statistics""" try: with database_connection() as conn: cursor = conn.cursor() stats = {} # Total tickets cursor.execute('SELECT COUNT(*) as count FROM tickets') stats['total'] = cursor.fetchone()['count'] # Tickets by status cursor.execute('SELECT status, COUNT(*) as count FROM tickets GROUP BY status') status_counts = cursor.fetchall() for row in status_counts: stats[row['status']] = row['count'] return stats except Exception as e: logger.error(f"Error getting ticket stats: {e}") return {}