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
}
