package storage

import (
	"context"
	"errors"

	"github.com/jackc/pgx/v5"
	"github.com/nomadcode/nomadcode-core/internal/db"
)

// ErrNoAvailableWorkspaceSlot is returned when no slot is in the available
// state for the requested project sync setting.
var ErrNoAvailableWorkspaceSlot = errors.New("no available workspace slot")

// ListWorkspaceSlots returns all slots registered for a project sync setting
// ordered by slot index.
func (s *Store) ListWorkspaceSlots(ctx context.Context, projectSyncSettingID int64) ([]db.WorkspaceSlot, error) {
	return s.queries.ListWorkspaceSlotsByProject(ctx, projectSyncSettingID)
}

// UpsertWorkspaceSlot registers a slot for a project sync setting, or updates
// its path if the (project, index) pair already exists. The slot state is left
// untouched on conflict so an in-use slot is never reset by registration.
// Callers build args via projectsync.ToUpsertWorkspaceSlotParams to enforce the
// index range.
func (s *Store) UpsertWorkspaceSlot(ctx context.Context, args db.UpsertWorkspaceSlotParams) (db.WorkspaceSlot, error) {
	return s.queries.UpsertWorkspaceSlot(ctx, args)
}

// ReserveWorkspaceSlot atomically transitions a single available slot to
// in_use and returns it. Concurrent callers never receive the same slot
// because the underlying query reserves one row with FOR UPDATE SKIP LOCKED.
// When no slot is available it returns ErrNoAvailableWorkspaceSlot.
func (s *Store) ReserveWorkspaceSlot(ctx context.Context, projectSyncSettingID int64) (db.WorkspaceSlot, error) {
	slot, err := s.queries.ReserveAvailableWorkspaceSlot(ctx, projectSyncSettingID)
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return db.WorkspaceSlot{}, ErrNoAvailableWorkspaceSlot
		}
		return db.WorkspaceSlot{}, err
	}
	return slot, nil
}

// UpdateWorkspaceSlotState transitions a slot to a new state. Callers build
// args via projectsync.ToUpdateWorkspaceSlotStateParams, which rejects unknown
// states to mirror the DB check constraint.
func (s *Store) UpdateWorkspaceSlotState(ctx context.Context, args db.UpdateWorkspaceSlotStateParams) (db.WorkspaceSlot, error) {
	return s.queries.UpdateWorkspaceSlotState(ctx, args)
}
