package library

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"time"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"

	"git.fabledsword.com/bvandeusen/minstrel/internal/coverart"
	"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)

// StuckScanThreshold is the age past which an in-flight scan_runs row is
// reaped on the next TryStartScan attempt. Real scans on realistic libraries
// finish well within this; the threshold is set far above legitimate scan
// duration so a normally-running scan is never reaped accidentally.
const StuckScanThreshold = 1 * time.Hour

// TryStartScan attempts to start a new scan, with built-in handling for
// stuck/stale in-flight rows.
//
// Behavior:
//   - No in-flight row → starts RunScan in a detached goroutine; returns (true, nil, nil).
//   - In-flight row, started_at > StuckScanThreshold ago → reaps via FinishScanRun
//     with error_message="reaped (stale)", then starts new scan; returns (true, nil, nil).
//   - In-flight row, started_at within threshold → returns (false, &row, nil); caller
//     decides what to do (HTTP handler returns 409; scheduler logs skip).
//   - DB error during the in-flight check or reap → returns (false, nil, err).
func TryStartScan(ctx context.Context, pool *pgxpool.Pool,
	scanner *Scanner, enricher *coverart.Enricher,
	logger *slog.Logger, cfg RunScanConfig,
) (started bool, existing *dbq.GetInFlightScanRunRow, err error) {
	q := dbq.New(pool)
	row, qerr := q.GetInFlightScanRun(ctx)
	if qerr == nil {
		// Found an in-flight row. Reap if stale, else skip.
		age := time.Since(row.StartedAt.Time)
		if age <= StuckScanThreshold {
			return false, &row, nil
		}
		logger.Warn("reaping stale in-flight scan",
			"id", row.ID, "started_at", row.StartedAt.Time, "age", age)
		if rerr := q.FinishScanRun(ctx, dbq.FinishScanRunParams{
			ID:      row.ID,
			Column2: "reaped (stale)",
		}); rerr != nil {
			return false, nil, fmt.Errorf("reap stale scan: %w", rerr)
		}
		// Fall through to start a new scan.
	} else if !errors.Is(qerr, pgx.ErrNoRows) {
		return false, nil, fmt.Errorf("in-flight check: %w", qerr)
	}

	// No in-flight (or just reaped). Start RunScan detached.
	go func() {
		if _, runErr := RunScan(ctx, pool, scanner, enricher, logger, cfg); runErr != nil {
			logger.Warn("scan run failed", "err", runErr)
		}
	}()
	return true, nil, nil
}
