package automation

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"gorm.io/gorm"
)

func (s *Store) CompleteRunIfTerminal(ctx context.Context, workspaceID string, runID string, now time.Time) (bool, error) {
	if err := s.ensureDB(); err != nil {
		return false, err
	}
	reconciled := false
	err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var run AutomationRun
		if err := tx.Where("workspace_id = ? AND id = ? AND status IN ?", strings.TrimSpace(workspaceID), strings.TrimSpace(runID), activeAutomationRunStatuses()).First(&run).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return nil
			}
			return fmt.Errorf("lock active automation run for terminal completion: %w", err)
		}
		changed, err := s.reconcileTerminalRunTx(tx, run, now)
		if err != nil {
			return err
		}
		reconciled = changed
		return nil
	})
	return reconciled, err
}

func (s *Store) FailRunAfterJobFailure(ctx context.Context, workspaceID string, runID string, failedJobRunID string, message string, now time.Time) (bool, error) {
	if err := s.ensureDB(); err != nil {
		return false, err
	}
	return s.failRunAfterJobFailure(ctx, workspaceID, runID, failedJobRunID, automationFailureMetadataJSON(message, nil), now)
}

func (s *Store) CancelRunRuntime(ctx context.Context, workspaceID string, runID string, actorPrincipalID string, reason string, now time.Time) error {
	if err := s.ensureDB(); err != nil {
		return err
	}
	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var run AutomationRun
		if err := tx.Where("workspace_id = ? AND id = ? AND status IN ?", strings.TrimSpace(workspaceID), strings.TrimSpace(runID), activeAutomationRunStatuses()).First(&run).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return nil
			}
			return err
		}
		metadataJSON := automationJSON(map[string]any{"message": strings.TrimSpace(reason)})
		if err := tx.Model(&AutomationJob{}).Where("workspace_id = ? AND run_id = ? AND status IN ?", run.WorkspaceID, run.ID, []string{AutomationJobStatusReady, AutomationJobStatusLeased}).Updates(map[string]any{
			"status":     AutomationJobStatusCanceled,
			"updated_at": now,
		}).Error; err != nil {
			return fmt.Errorf("cancel automation job assignments: %w", err)
		}
		if err := tx.Model(&AutomationWait{}).Where("workspace_id = ? AND run_id = ? AND status = ?", run.WorkspaceID, run.ID, AutomationWaitStatusActive).Updates(map[string]any{
			"status":      AutomationWaitStatusCanceled,
			"active_key":  nil,
			"result_json": metadataJSON,
			"updated_at":  now,
			"canceled_at": now,
		}).Error; err != nil {
			return fmt.Errorf("cancel automation waits: %w", err)
		}
		var jobRuns []AutomationJobRun
		if err := tx.Where("workspace_id = ? AND run_id = ?", run.WorkspaceID, run.ID).Find(&jobRuns).Error; err != nil {
			return fmt.Errorf("list automation job runs for cancel: %w", err)
		}
		for _, jobRun := range jobRuns {
			if isTerminalJobRunStatus(jobRun.Status) {
				continue
			}
			jobRun.Status = AutomationJobRunStatusCanceled
			jobRun.ErrorJSON = metadataJSON
			jobRun.CompletedAt = &now
			jobRun.UpdatedAt = now
			if err := tx.Save(&jobRun).Error; err != nil {
				return fmt.Errorf("cancel automation job run: %w", err)
			}
		}
		result := tx.Model(&AutomationRun{}).Where("workspace_id = ? AND id = ? AND status IN ?", run.WorkspaceID, run.ID, activeAutomationRunStatuses()).Updates(map[string]any{
			"status":       AutomationRunStatusCanceled,
			"error_json":   metadataJSON,
			"completed_at": now,
			"updated_at":   now,
		})
		if result.Error != nil {
			return fmt.Errorf("cancel automation run: %w", result.Error)
		}
		if result.RowsAffected == 0 {
			return nil
		}
		run.Status = AutomationRunStatusCanceled
		run.ErrorJSON = metadataJSON
		run.CompletedAt = &now
		run.UpdatedAt = now
		if err := releaseActiveRunSlotTx(tx, run.WorkspaceID, run.ID); err != nil {
			return err
		}
		if err := enqueueAutomationRepoStatusTerminalTx(tx, run, now); err != nil {
			return err
		}
		event := AutomationEvent{
			ID:               automationEventID("run_canceled", run.ID, now),
			WorkspaceID:      run.WorkspaceID,
			RunID:            run.ID,
			EventType:        "run_canceled",
			ActorPrincipalID: strings.TrimSpace(actorPrincipalID),
			Summary:          "Automation run canceled",
			MetadataJSON:     metadataJSON,
			CreatedAt:        now,
		}
		if err := tx.Create(&event).Error; err != nil {
			return fmt.Errorf("create automation cancel event: %w", err)
		}
		return nil
	})
}
