package services import ( "context" "database/sql" "errors" "github.com/rede5/gohorsejobs/backend/internal/models" ) type TicketService struct { DB *sql.DB } func NewTicketService(db *sql.DB) *TicketService { return &TicketService{DB: db} } func (s *TicketService) CreateTicket(ctx context.Context, userID int, subject, priority string) (*models.Ticket, error) { if priority == "" { priority = "medium" } query := ` INSERT INTO tickets (user_id, subject, status, priority, created_at, updated_at) VALUES ($1, $2, 'open', $3, NOW(), NOW()) RETURNING id, user_id, subject, status, priority, created_at, updated_at ` var t models.Ticket err := s.DB.QueryRowContext(ctx, query, userID, subject, priority).Scan( &t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt, ) if err != nil { return nil, err } return &t, nil } func (s *TicketService) ListTickets(ctx context.Context, userID int) ([]models.Ticket, error) { query := ` SELECT id, user_id, subject, status, priority, created_at, updated_at FROM tickets WHERE user_id = $1 ORDER BY updated_at DESC ` rows, err := s.DB.QueryContext(ctx, query, userID) if err != nil { return nil, err } defer rows.Close() var tickets []models.Ticket for rows.Next() { var t models.Ticket if err := rows.Scan( &t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt, ); err != nil { return nil, err } tickets = append(tickets, t) } return tickets, nil } func (s *TicketService) GetTicket(ctx context.Context, ticketID string, userID int) (*models.Ticket, []models.TicketMessage, error) { // 1. Get Ticket queryTicket := ` SELECT id, user_id, subject, status, priority, created_at, updated_at FROM tickets WHERE id = $1 AND user_id = $2 ` var t models.Ticket err := s.DB.QueryRowContext(ctx, queryTicket, ticketID, userID).Scan( &t.ID, &t.UserID, &t.Subject, &t.Status, &t.Priority, &t.CreatedAt, &t.UpdatedAt, ) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil, errors.New("ticket not found") } return nil, nil, err } // 2. Get Messages queryMsgs := ` SELECT id, ticket_id, user_id, message, created_at FROM ticket_messages WHERE ticket_id = $1 ORDER BY created_at ASC ` rows, err := s.DB.QueryContext(ctx, queryMsgs, ticketID) if err != nil { return nil, nil, err } defer rows.Close() var messages []models.TicketMessage for rows.Next() { var m models.TicketMessage if err := rows.Scan( &m.ID, &m.TicketID, &m.UserID, &m.Message, &m.CreatedAt, ); err != nil { return nil, nil, err } messages = append(messages, m) } return &t, messages, nil } func (s *TicketService) AddMessage(ctx context.Context, ticketID string, userID int, message string) (*models.TicketMessage, error) { // Verify ticket ownership first (or admin access, but keeping simple for now) var count int err := s.DB.QueryRowContext(ctx, "SELECT COUNT(*) FROM tickets WHERE id = $1 AND user_id = $2", ticketID, userID).Scan(&count) if err != nil { return nil, err } if count == 0 { return nil, errors.New("ticket not found") } query := ` INSERT INTO ticket_messages (ticket_id, user_id, message, created_at) VALUES ($1, $2, $3, NOW()) RETURNING id, ticket_id, user_id, message, created_at ` var m models.TicketMessage err = s.DB.QueryRowContext(ctx, query, ticketID, userID, message).Scan( &m.ID, &m.TicketID, &m.UserID, &m.Message, &m.CreatedAt, ) if err != nil { return nil, err } // Update ticket updated_at _, _ = s.DB.ExecContext(ctx, "UPDATE tickets SET updated_at = NOW() WHERE id = $1", ticketID) return &m, nil }