package statemachine

import (
	"fmt"
	"time"

	"go.temporal.io/sdk/workflow"
)

// TaskUnitWorkflow executes a single task with retries and judge review.
// Flow: Worktree → Implementer (retry) → Test → Judge → Commit or Retry
func TaskUnitWorkflow(ctx workflow.Context, in TaskUnitInput) (TaskUnitOutput, error) {
	logger := workflow.GetLogger(ctx)
	output := TaskUnitOutput{
		TaskID:  in.TaskID,
		Status:  "failed",
		Reason:  "",
		Branch:  fmt.Sprintf("task/%s", in.TaskID),
		Changes: "",
	}

	activityOpts := workflow.ActivityOptions{
		StartToCloseTimeout:    30 * time.Minute,
		ScheduleToCloseTimeout: 35 * time.Minute,
	}
	actCtx := workflow.WithActivityOptions(ctx, activityOpts)

	// Add worktree
	var worktreePath string
	if err := workflow.ExecuteActivity(actCtx, "GitWorktreeAddActivity", map[string]interface{}{
		"RepoPath": in.TargetRepoPath,
		"TaskID":   in.TaskID,
	}).Get(ctx, &worktreePath); err != nil {
		output.Reason = fmt.Sprintf("worktree add failed: %v", err)
		return output, nil
	}

	logger.Info("task unit started", "task", in.TaskID, "worktree", worktreePath)

	// Retry loop: implementer + test + judge
	maxRetries := 3
	var lessons string

	for attempt := 1; attempt <= maxRetries; attempt++ {
		logger.Info("attempt", "task", in.TaskID, "attempt", attempt)

		// Call ImplementerActivity with escalating timeout
		timeoutMultiplier := int64(attempt)
		implOpts := workflow.ActivityOptions{
			StartToCloseTimeout:    time.Duration(timeoutMultiplier*30) * time.Minute,
			ScheduleToCloseTimeout: time.Duration(timeoutMultiplier*35) * time.Minute,
		}
		implCtx := workflow.WithActivityOptions(ctx, implOpts)

		var implOutput map[string]interface{}
		implErr := workflow.ExecuteActivity(implCtx, "ImplementerActivity", map[string]interface{}{
			"Config":       in.Config,
			"TaskID":       in.TaskID,
			"WorktreePath": worktreePath,
			"Lessons":      lessons,
		}).Get(ctx, &implOutput)

		if implErr != nil {
			if attempt < maxRetries {
				logger.Info("implementer failed, will retry", "task", in.TaskID, "attempt", attempt, "error", implErr)
				continue
			}
			output.Reason = fmt.Sprintf("implementer exhausted after %d attempts: %v", maxRetries, implErr)
			return output, nil
		}

		// Run integration test
		var testOutput map[string]interface{}
		if err := workflow.ExecuteActivity(actCtx, "RunIntegrationTestActivity", map[string]interface{}{
			"WorktreePath": worktreePath,
			"TestCmd":      "go test ./...",
		}).Get(ctx, &testOutput); err != nil {
			logger.Info("test failed", "task", in.TaskID, "error", err)
			testOutput = map[string]interface{}{
				"success": false,
				"logs":    fmt.Sprintf("test error: %v", err),
			}
		}

		// Get diff
		var diff string
		if err := workflow.ExecuteActivity(actCtx, "GitDiffActivity", map[string]interface{}{
			"WorktreePath": worktreePath,
		}).Get(ctx, &diff); err != nil {
			logger.Info("diff failed", "task", in.TaskID, "error", err)
		}

		// Call JudgeActivity
		var judgeOutput map[string]interface{}
		if err := workflow.ExecuteActivity(actCtx, "JudgeActivity", map[string]interface{}{
			"Config":              in.Config,
			"Diff":                diff,
			"IntegrationTestLogs": fmt.Sprintf("%v", testOutput),
		}).Get(ctx, &judgeOutput); err != nil {
			logger.Info("judge failed", "task", in.TaskID, "error", err)
		}

		verdict, _ := judgeOutput["Verdict"].(string)
		critique, _ := judgeOutput["Critique"].(string)

		if verdict == "pass" {
			// Commit and success
			if err := workflow.ExecuteActivity(actCtx, "GitCommitActivity", map[string]interface{}{
				"WorktreePath": worktreePath,
				"Message":      fmt.Sprintf("%s: implementation", in.TaskID),
			}).Get(ctx, nil); err != nil {
				output.Reason = fmt.Sprintf("commit failed: %v", err)
				return output, nil
			}

			output.Status = "success"
			output.Changes = fmt.Sprintf("completed in %d attempt(s)", attempt)
			logger.Info("task success", "task", in.TaskID, "attempt", attempt)
			return output, nil
		}

		// Judge failed: append to lessons and retry
		if attempt < maxRetries {
			lessons = fmt.Sprintf("%s\nAttempt %d critique: %s", lessons, attempt, critique)
			logger.Info("judge rejected, appending to lessons and retrying", "task", in.TaskID, "attempt", attempt)
			continue
		}

		// Exhausted retries after judge failures
		output.Reason = fmt.Sprintf("judge rejected after %d attempts. Last critique: %s", maxRetries, critique)
		logger.Info("judge exhausted retries", "task", in.TaskID)
		return output, nil
	}

	output.Reason = "exhausted all retry attempts"
	return output, nil
}
