package repositories import ( "context" "fmt" "strings" "github.com/CubeCraft-Creations/Extrudex/backend/internal/models" "github.com/jackc/pgx/v5/pgxpool" ) // PrintJobRepository handles database queries for print_jobs. type PrintJobRepository struct { pool *pgxpool.Pool } // NewPrintJobRepository creates a PrintJobRepository backed by the given pool. func NewPrintJobRepository(pool *pgxpool.Pool) *PrintJobRepository { return &PrintJobRepository{pool: pool} } // PrintJobFilter holds query parameters for listing print jobs. type PrintJobFilter struct { Status string // filter by job_status name (case-insensitive) PrinterID *int // filter by printer_id Limit int Offset int } // scanPrintJobWithJoins scans a print_job row with JOINed tables. func (r *PrintJobRepository) scanPrintJobWithJoins(row interface{ Scan(...interface{}) error }) (models.PrintJob, error) { var pj models.PrintJob var js models.JobStatus err := row.Scan( &pj.ID, &pj.PrinterID, &pj.FilamentSpoolID, &pj.JobName, &pj.FileName, &pj.JobStatusID, &pj.StartedAt, &pj.CompletedAt, &pj.DurationSeconds, &pj.EstimatedDurationSeconds, &pj.TotalMMExtruded, &pj.TotalGramsUsed, &pj.TotalCostUSD, &pj.Notes, &pj.DeletedAt, &pj.CreatedAt, &pj.UpdatedAt, &js.ID, &js.Name, &js.CreatedAt, &js.UpdatedAt, ) if err != nil { return pj, err } pj.JobStatus = &js return pj, nil } // GetAll returns print jobs matching the given filters, with pagination. func (r *PrintJobRepository) GetAll(ctx context.Context, filter PrintJobFilter) ([]models.PrintJob, int, error) { conditions := []string{"pj.deleted_at IS NULL"} args := []interface{}{} argIdx := 1 if filter.Status != "" { conditions = append(conditions, fmt.Sprintf("LOWER(js.name) = LOWER($%d)", argIdx)) args = append(args, filter.Status) argIdx++ } if filter.PrinterID != nil { conditions = append(conditions, fmt.Sprintf("pj.printer_id = $%d", argIdx)) args = append(args, *filter.PrinterID) argIdx++ } whereClause := "" if len(conditions) > 0 { whereClause = "WHERE " + strings.Join(conditions, " AND ") } // Count. var total int countQuery := `SELECT COUNT(*) FROM print_jobs pj LEFT JOIN job_statuses js ON pj.job_status_id = js.id ` + " " + whereClause if err := r.pool.QueryRow(ctx, countQuery, args...).Scan(&total); err != nil { return nil, 0, err } // Query with pagination. dataQuery := `SELECT pj.id, pj.printer_id, pj.filament_spool_id, pj.job_name, pj.file_name, pj.job_status_id, pj.started_at, pj.completed_at, pj.duration_seconds, pj.estimated_duration_seconds, pj.total_mm_extruded, pj.total_grams_used, pj.total_cost_usd, pj.notes, pj.deleted_at, pj.created_at, pj.updated_at, js.id, js.name, js.created_at, js.updated_at FROM print_jobs pj LEFT JOIN job_statuses js ON pj.job_status_id = js.id ` + whereClause + " ORDER BY pj.created_at DESC" + fmt.Sprintf(" LIMIT $%d OFFSET $%d", argIdx, argIdx+1) dataArgs := make([]interface{}, len(args)) copy(dataArgs, args) dataArgs = append(dataArgs, filter.Limit, filter.Offset) rows, err := r.pool.Query(ctx, dataQuery, dataArgs...) if err != nil { return nil, 0, err } defer rows.Close() var jobs []models.PrintJob for rows.Next() { pj, err := r.scanPrintJobWithJoins(rows) if err != nil { return nil, 0, err } jobs = append(jobs, pj) } if err := rows.Err(); err != nil { return nil, 0, err } if jobs == nil { jobs = []models.PrintJob{} } return jobs, total, nil } // GetByID returns a single print job by ID with JOINed job_status. func (r *PrintJobRepository) GetByID(ctx context.Context, id int) (*models.PrintJob, error) { row := r.pool.QueryRow(ctx, ` SELECT pj.id, pj.printer_id, pj.filament_spool_id, pj.job_name, pj.file_name, pj.job_status_id, pj.started_at, pj.completed_at, pj.duration_seconds, pj.estimated_duration_seconds, pj.total_mm_extruded, pj.total_grams_used, pj.total_cost_usd, pj.notes, pj.deleted_at, pj.created_at, pj.updated_at, js.id, js.name, js.created_at, js.updated_at FROM print_jobs pj LEFT JOIN job_statuses js ON pj.job_status_id = js.id WHERE pj.id = $1 AND pj.deleted_at IS NULL `, id) pj, err := r.scanPrintJobWithJoins(row) if err != nil { return nil, err } return &pj, nil }