package runtime

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

	"github.com/tidbcloud/AgentTeam/internal/store/automation"
)

type Reconciler struct {
	store  *automation.Store
	now    func() time.Time
	logger *slog.Logger
	mu     sync.Mutex
	status ReconcilerStatus
}

type ReconcileOptions struct {
	Limit        int
	WorkspaceIDs []string
}

type ReconcileResult struct {
	StartedAt              time.Time
	CompletedAt            time.Time
	RecoveredExpiredJobs   int64
	ReconciledDependencies int64
	ReconciledRuns         int64
	RepairedActiveRunSlots int64
}

// ReconcilerStatus is the in-memory heartbeat for the current process'
// automation reconciler agentteam.
type ReconcilerStatus struct {
	LastStartedAt       time.Time
	LastCompletedAt     time.Time
	LastSuccessAt       time.Time
	LastFailureAt       time.Time
	LastError           string
	ErrorCount          uint64
	ConsecutiveFailures uint64
	LastResult          ReconcileResult
}

func NewReconciler(store *automation.Store, now func() time.Time, logger *slog.Logger) *Reconciler {
	return &Reconciler{store: store, now: now, logger: logger}
}

func (r *Reconciler) Reconcile(ctx context.Context, options ReconcileOptions) (ReconcileResult, error) {
	if r == nil {
		return ReconcileResult{}, fmt.Errorf("automation reconciler store is nil")
	}
	started := r.currentTime()
	r.recordStarted(started)
	if r.store == nil {
		err := fmt.Errorf("automation reconciler store is nil")
		r.recordFailure(r.currentTime(), err)
		return ReconcileResult{}, err
	}
	limit := options.Limit
	if limit <= 0 {
		limit = 100
	}
	recovered, err := r.store.RecoverExpiredJobs(ctx, started, limit, options.WorkspaceIDs)
	if err != nil {
		r.recordFailure(r.currentTime(), err)
		r.log(ctx, slog.LevelError, "automation reconciler failed", "error", err)
		return ReconcileResult{}, err
	}
	dependencies, err := r.store.ReconcileBlockedDependencies(ctx, started, limit, options.WorkspaceIDs)
	if err != nil {
		r.recordFailure(r.currentTime(), err)
		r.log(ctx, slog.LevelError, "automation reconciler dependency reconciliation failed", "error", err)
		return ReconcileResult{}, err
	}
	reconciledRuns, err := r.store.ReconcileTerminalRuns(ctx, started, limit, options.WorkspaceIDs)
	if err != nil {
		r.recordFailure(r.currentTime(), err)
		r.log(ctx, slog.LevelError, "automation reconciler terminal run reconciliation failed", "error", err)
		return ReconcileResult{}, err
	}
	repairedSlots, err := r.store.RepairLeakedActiveRunSlots(ctx, limit, options.WorkspaceIDs)
	if err != nil {
		r.recordFailure(r.currentTime(), err)
		r.log(ctx, slog.LevelError, "automation reconciler active run slot repair failed", "error", err)
		return ReconcileResult{}, err
	}
	completed := r.currentTime()
	result := ReconcileResult{StartedAt: started, CompletedAt: completed, RecoveredExpiredJobs: recovered, ReconciledDependencies: dependencies, ReconciledRuns: reconciledRuns, RepairedActiveRunSlots: repairedSlots}
	r.recordSuccess(completed, result)
	r.log(ctx, slog.LevelInfo, "automation reconciler completed",
		"recovered_expired_jobs", recovered,
		"reconciled_dependencies", dependencies,
		"reconciled_runs", reconciledRuns,
		"repaired_active_run_slots", repairedSlots,
	)
	return result, nil
}

// Status returns a process-local reconciler heartbeat snapshot.
func (r *Reconciler) Status() ReconcilerStatus {
	if r == nil {
		return ReconcilerStatus{}
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	return r.status
}

func (r *Reconciler) currentTime() time.Time {
	if r.now != nil {
		return r.now()
	}
	return time.Now().UTC()
}

func (r *Reconciler) recordStarted(started time.Time) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.status.LastStartedAt = started
}

func (r *Reconciler) recordSuccess(completed time.Time, result ReconcileResult) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.status.LastCompletedAt = completed
	r.status.LastSuccessAt = completed
	r.status.LastError = ""
	r.status.ConsecutiveFailures = 0
	r.status.LastResult = result
}

func (r *Reconciler) recordFailure(failedAt time.Time, err error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.status.LastCompletedAt = failedAt
	r.status.LastFailureAt = failedAt
	if err != nil {
		r.status.LastError = err.Error()
	}
	r.status.ErrorCount++
	r.status.ConsecutiveFailures++
}

func (r *Reconciler) log(ctx context.Context, level slog.Level, message string, args ...any) {
	logger := r.logger
	if logger == nil {
		logger = slog.Default()
	}
	logger.Log(ctx, level, message, args...)
}
