package automation

import (
	"context"
	"errors"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/service/automation/runtime"
	automationstore "github.com/tidbcloud/AgentTeam/internal/store/automation"
	"gorm.io/gorm"
)

const automationWaitSignalDeliveryBatchSize = 100

func (s *AutomationsService) validateAutomationWaitSignalResumeScope(ctx context.Context, workspaceID string, subject string) error {
	if err := s.validateScope(workspaceID, subject); err != nil {
		return err
	}
	if err := s.automationAuthorizer().CanResumeWait(ctx, s.automationAuthorizationRequest(ctx, workspaceID, subject, AutomationAuthorizationRequest{})); err != nil {
		return err
	}
	return nil
}

func (s *AutomationsService) resumeAutomationWaitSignalMatches(ctx context.Context, workspaceID string, subject string, signal automationstore.AutomationWaitSignal, resultJSON string, eventer *runtime.Eventer, active bool, processedWaitIDs map[string]struct{}) (automationstore.AutomationWait, bool, error) {
	var cursor automationstore.AutomationWaitSignalCursor
	var firstResolved automationstore.AutomationWait
	for {
		waitRuns, err := s.listAutomationWaitRunsForSignal(ctx, workspaceID, signal, cursor, active)
		if err != nil {
			return automationstore.AutomationWait{}, false, err
		}
		if len(waitRuns) == 0 {
			return firstResolved, firstResolved.ID != "", nil
		}
		for _, waitRun := range waitRuns {
			waitID := strings.TrimSpace(waitRun.Wait.ID)
			if _, ok := processedWaitIDs[waitID]; ok {
				continue
			}
			if err := s.automationAuthorizer().CanResumeWait(ctx, s.automationAuthorizationRequest(ctx, workspaceID, subject, AutomationAuthorizationRequest{Run: waitRun.Run, Wait: waitRun.Wait})); err != nil {
				return automationstore.AutomationWait{}, false, err
			}
			resolved, err := eventer.ResumeWait(ctx, strings.TrimSpace(workspaceID), waitID, strings.TrimSpace(subject), resultJSON)
			if err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					return automationstore.AutomationWait{}, false, notFound("Automation wait signal not found")
				}
				return automationstore.AutomationWait{}, false, err
			}
			if processedWaitIDs != nil {
				processedWaitIDs[waitID] = struct{}{}
			}
			if firstResolved.ID == "" {
				firstResolved = resolved
			}
		}
		last := waitRuns[len(waitRuns)-1].Wait
		cursor = automationstore.AutomationWaitSignalCursor{UpdatedAt: last.UpdatedAt, ID: last.ID}
		if len(waitRuns) < automationWaitSignalDeliveryBatchSize {
			return firstResolved, firstResolved.ID != "", nil
		}
	}
}

func (s *AutomationsService) listAutomationWaitRunsForSignal(ctx context.Context, workspaceID string, signal automationstore.AutomationWaitSignal, cursor automationstore.AutomationWaitSignalCursor, active bool) ([]automationstore.AutomationWaitRun, error) {
	var (
		waitRuns []automationstore.AutomationWaitRun
		err      error
	)
	if active {
		waitRuns, err = s.store.ListActiveWaitRunsBySignal(ctx, strings.TrimSpace(workspaceID), signal, cursor, automationWaitSignalDeliveryBatchSize)
	} else {
		waitRuns, err = s.store.ListResolvedWaitRunsBySignalNeedingSchedulerReplay(ctx, strings.TrimSpace(workspaceID), signal, cursor, automationWaitSignalDeliveryBatchSize)
	}
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, notFound("Automation run not found")
	}
	return waitRuns, err
}
