package automation

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

	"gorm.io/gorm"
)

type QueueMetrics struct {
	Ready         int64
	Leased        int64
	Done          int64
	Failed        int64
	Canceled      int64
	ExpiredLeases int64
	RetryReady    int64
}

type RunMetrics struct {
	Queued               int64
	Running              int64
	Waiting              int64
	Blocked              int64
	Failed               int64
	Succeeded            int64
	Canceled             int64
	ActiveSlots          int64
	LeakedActiveSlots    int64
	TerminalInconsistent int64
}

type WaitMetrics struct {
	Active           int64
	Resolved         int64
	Canceled         int64
	ActiveAgentTasks int64
	ActiveEventWaits int64
}

type RunnerMetrics struct {
	Registered      int64
	Active          int64
	Revoked         int64
	ActiveSessions  int64
	ExpiredSessions int64
	RevokedSessions int64
}

func (s *Store) QueueMetrics(ctx context.Context, workspaceID string, now time.Time) (QueueMetrics, error) {
	if err := s.ensureDB(); err != nil {
		return QueueMetrics{}, err
	}
	var metrics QueueMetrics
	count := func(target *int64, query *gorm.DB) error {
		if err := query.Count(target).Error; err != nil {
			return err
		}
		return nil
	}
	base := s.db.WithContext(ctx).Model(&AutomationJob{})
	if workspaceID = strings.TrimSpace(workspaceID); workspaceID != "" {
		base = base.Where("workspace_id = ?", workspaceID)
	}
	if err := count(&metrics.Ready, base.Session(&gorm.Session{}).Where("status = ?", AutomationJobStatusReady)); err != nil {
		return QueueMetrics{}, fmt.Errorf("count ready automation jobs: %w", err)
	}
	if err := count(&metrics.Leased, base.Session(&gorm.Session{}).Where("status = ?", AutomationJobStatusLeased)); err != nil {
		return QueueMetrics{}, fmt.Errorf("count leased automation jobs: %w", err)
	}
	if err := count(&metrics.Done, base.Session(&gorm.Session{}).Where("status = ?", AutomationJobStatusDone)); err != nil {
		return QueueMetrics{}, fmt.Errorf("count done automation jobs: %w", err)
	}
	if err := count(&metrics.Failed, base.Session(&gorm.Session{}).Where("status = ?", AutomationJobStatusFailed)); err != nil {
		return QueueMetrics{}, fmt.Errorf("count failed automation jobs: %w", err)
	}
	if err := count(&metrics.Canceled, base.Session(&gorm.Session{}).Where("status = ?", AutomationJobStatusCanceled)); err != nil {
		return QueueMetrics{}, fmt.Errorf("count canceled automation jobs: %w", err)
	}
	if err := count(&metrics.ExpiredLeases, base.Session(&gorm.Session{}).Where("status = ? AND lease_until IS NOT NULL AND lease_until < ?", AutomationJobStatusLeased, now)); err != nil {
		return QueueMetrics{}, fmt.Errorf("count expired automation job leases: %w", err)
	}
	if err := count(&metrics.RetryReady, base.Session(&gorm.Session{}).Where("status = ? AND attempt > 0", AutomationJobStatusReady)); err != nil {
		return QueueMetrics{}, fmt.Errorf("count retry-ready automation jobs: %w", err)
	}
	return metrics, nil
}

func (s *Store) RunMetrics(ctx context.Context, workspaceID string) (RunMetrics, error) {
	if err := s.ensureDB(); err != nil {
		return RunMetrics{}, err
	}
	var metrics RunMetrics
	count := func(target *int64, query *gorm.DB) error {
		if err := query.Count(target).Error; err != nil {
			return err
		}
		return nil
	}
	base := s.db.WithContext(ctx).Model(&AutomationRun{})
	if workspaceID = strings.TrimSpace(workspaceID); workspaceID != "" {
		base = base.Where("workspace_id = ?", workspaceID)
	}
	for status, target := range map[string]*int64{
		AutomationRunStatusQueued:    &metrics.Queued,
		AutomationRunStatusRunning:   &metrics.Running,
		AutomationRunStatusWaiting:   &metrics.Waiting,
		AutomationRunStatusBlocked:   &metrics.Blocked,
		AutomationRunStatusFailed:    &metrics.Failed,
		AutomationRunStatusSucceeded: &metrics.Succeeded,
		AutomationRunStatusCanceled:  &metrics.Canceled,
	} {
		if err := count(target, base.Session(&gorm.Session{}).Where("status = ?", status)); err != nil {
			return RunMetrics{}, fmt.Errorf("count %s automation runs: %w", status, err)
		}
	}
	inconsistent, err := s.countTerminalInconsistentRuns(ctx, workspaceID)
	if err != nil {
		return RunMetrics{}, err
	}
	metrics.TerminalInconsistent = inconsistent
	activeSlots, err := s.countActiveRunSlots(ctx, workspaceID)
	if err != nil {
		return RunMetrics{}, err
	}
	metrics.ActiveSlots = activeSlots
	leakedSlots, err := s.countLeakedActiveRunSlots(ctx, workspaceID)
	if err != nil {
		return RunMetrics{}, err
	}
	metrics.LeakedActiveSlots = leakedSlots
	return metrics, nil
}

func (s *Store) WaitMetrics(ctx context.Context, workspaceID string) (WaitMetrics, error) {
	if err := s.ensureDB(); err != nil {
		return WaitMetrics{}, err
	}
	var metrics WaitMetrics
	count := func(target *int64, query *gorm.DB) error {
		if err := query.Count(target).Error; err != nil {
			return err
		}
		return nil
	}
	base := s.db.WithContext(ctx).Model(&AutomationWait{})
	if workspaceID = strings.TrimSpace(workspaceID); workspaceID != "" {
		base = base.Where("workspace_id = ?", workspaceID)
	}
	if err := count(&metrics.Active, base.Session(&gorm.Session{}).Where("status = ?", AutomationWaitStatusActive)); err != nil {
		return WaitMetrics{}, fmt.Errorf("count active automation waits: %w", err)
	}
	if err := count(&metrics.Resolved, base.Session(&gorm.Session{}).Where("status = ?", AutomationWaitStatusResolved)); err != nil {
		return WaitMetrics{}, fmt.Errorf("count resolved automation waits: %w", err)
	}
	if err := count(&metrics.Canceled, base.Session(&gorm.Session{}).Where("status = ?", AutomationWaitStatusCanceled)); err != nil {
		return WaitMetrics{}, fmt.Errorf("count canceled automation waits: %w", err)
	}
	if err := count(&metrics.ActiveAgentTasks, base.Session(&gorm.Session{}).Where("status = ? AND wait_kind = ?", AutomationWaitStatusActive, AutomationWaitKindAgentTask)); err != nil {
		return WaitMetrics{}, fmt.Errorf("count active automation agent tasks: %w", err)
	}
	if err := count(&metrics.ActiveEventWaits, base.Session(&gorm.Session{}).Where("status = ? AND wait_kind = ?", AutomationWaitStatusActive, AutomationWaitKindWaitEvent)); err != nil {
		return WaitMetrics{}, fmt.Errorf("count active automation event waits: %w", err)
	}
	return metrics, nil
}

func (s *Store) RunnerMetrics(ctx context.Context, workspaceID string, now time.Time) (RunnerMetrics, error) {
	if err := s.ensureDB(); err != nil {
		return RunnerMetrics{}, err
	}
	var metrics RunnerMetrics
	count := func(target *int64, query *gorm.DB) error {
		if err := query.Count(target).Error; err != nil {
			return err
		}
		return nil
	}
	runners := s.db.WithContext(ctx).Model(&AutomationRunner{})
	sessions := s.db.WithContext(ctx).Model(&AutomationRunnerSession{})
	if workspaceID = strings.TrimSpace(workspaceID); workspaceID != "" {
		runners = runners.Where("workspace_id = ?", workspaceID)
		sessions = sessions.Where("workspace_id = ?", workspaceID)
	}
	if err := count(&metrics.Registered, runners.Session(&gorm.Session{})); err != nil {
		return RunnerMetrics{}, fmt.Errorf("count automation runners: %w", err)
	}
	if err := count(&metrics.Active, runners.Session(&gorm.Session{}).Where("status = ? AND revoked_at IS NULL", AutomationRunnerStatusActive)); err != nil {
		return RunnerMetrics{}, fmt.Errorf("count active automation runners: %w", err)
	}
	if err := count(&metrics.Revoked, runners.Session(&gorm.Session{}).Where("(status = ? OR revoked_at IS NOT NULL)", AutomationRunnerStatusRevoked)); err != nil {
		return RunnerMetrics{}, fmt.Errorf("count revoked automation runners: %w", err)
	}
	if err := count(&metrics.ActiveSessions, sessions.Session(&gorm.Session{}).Where("status = ? AND revoked_at IS NULL AND expires_at > ?", AutomationRunnerSessionStatusActive, now)); err != nil {
		return RunnerMetrics{}, fmt.Errorf("count active automation runner sessions: %w", err)
	}
	if err := count(&metrics.ExpiredSessions, sessions.Session(&gorm.Session{}).Where("status = ? AND revoked_at IS NULL AND expires_at <= ?", AutomationRunnerSessionStatusActive, now)); err != nil {
		return RunnerMetrics{}, fmt.Errorf("count expired automation runner sessions: %w", err)
	}
	if err := count(&metrics.RevokedSessions, sessions.Session(&gorm.Session{}).Where("(status = ? OR revoked_at IS NOT NULL)", AutomationRunnerSessionStatusRevoked)); err != nil {
		return RunnerMetrics{}, fmt.Errorf("count revoked automation runner sessions: %w", err)
	}
	return metrics, nil
}

// CreateRunWithRuntime persists a run, initial job run rows, events, queue rows,
