package automation

import (
	"context"
	"errors"
	"fmt"

	"gorm.io/gorm"
)

// CreateRunWithRuntime persists a run, initial job run rows, events, queue rows,
// and AgentTeam-native waits in one transaction.
func (s *Store) CreateRunWithRuntime(ctx context.Context, run AutomationRun, jobRuns []AutomationJobRun, events []AutomationEvent, jobs []AutomationJob, waitGroups ...[]AutomationWait) (AutomationRun, error) {
	return s.createRunWithRuntime(ctx, 0, run, jobRuns, events, jobs, waitGroups...)
}

// CreateRunWithRuntimeWithCapacity persists a run and reserves an active-run
// slot in the same transaction when activeRunLimit is positive.
func (s *Store) CreateRunWithRuntimeWithCapacity(ctx context.Context, activeRunLimit int, run AutomationRun, jobRuns []AutomationJobRun, events []AutomationEvent, jobs []AutomationJob, waitGroups ...[]AutomationWait) (AutomationRun, error) {
	return s.createRunWithRuntime(ctx, activeRunLimit, run, jobRuns, events, jobs, waitGroups...)
}

func (s *Store) createRunWithRuntime(ctx context.Context, activeRunLimit int, run AutomationRun, jobRuns []AutomationJobRun, events []AutomationEvent, jobs []AutomationJob, waitGroups ...[]AutomationWait) (AutomationRun, error) {
	if err := s.ensureDB(); err != nil {
		return AutomationRun{}, err
	}
	idempotencyKey := automationRunIdempotencyKey(run)
	if idempotencyKey != "" {
		existing, err := s.GetRunByIdempotencyKey(ctx, run.WorkspaceID, idempotencyKey)
		if err == nil {
			return existing, nil
		}
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return AutomationRun{}, fmt.Errorf("get idempotent automation run: %w", err)
		}
	}
	err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if !isTerminalRunStatus(run.Status) {
			if err := reserveActiveRunSlotTx(tx, run.WorkspaceID, run.ID, activeRunLimit, run.CreatedAt); err != nil {
				return err
			}
		}
		if err := tx.Create(&run).Error; err != nil {
			return err
		}
		if isTerminalRunStatus(run.Status) {
			if err := enqueueAutomationRepoStatusTerminalTx(tx, run, run.CreatedAt); err != nil {
				return err
			}
		} else {
			if err := upsertAutomationRepoStatusTx(tx, run, AutomationRepoStatusStatePending, run.CreatedAt); err != nil {
				return err
			}
		}
		if len(jobRuns) > 0 {
			if err := tx.Create(&jobRuns).Error; err != nil {
				return err
			}
		}
		if len(events) > 0 {
			if err := tx.Create(&events).Error; err != nil {
				return err
			}
		}
		for _, waits := range waitGroups {
			if len(waits) > 0 {
				for i := range waits {
					normalizeAutomationWaitSignalKey(&waits[i])
				}
				if err := tx.Create(&waits).Error; err != nil {
					return err
				}
			}
		}
		for _, job := range jobs {
			NormalizeAutomationJobClaimFields(&job)
			var existing AutomationJob
			err := tx.Where("idempotency_key = ?", job.IdempotencyKey).First(&existing).Error
			if err == nil {
				continue
			}
			if !errors.Is(err, gorm.ErrRecordNotFound) {
				return err
			}
			if err := tx.Create(&job).Error; err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		if idempotencyKey != "" && isUniqueConstraintError(err) {
			existing, getErr := s.GetRunByIdempotencyKey(ctx, run.WorkspaceID, idempotencyKey)
			if getErr == nil {
				return existing, nil
			}
		}
		return AutomationRun{}, fmt.Errorf("create automation run runtime: %w", err)
	}
	return run, nil
}
