package service

import (
	"context"
	"fmt"
	"time"

	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/logging"
	"github.com/orchard9/rdev/internal/port"
	"github.com/orchard9/rdev/internal/sdlc"
)

// GitCommitResult describes the outcome of a git commit+push operation.
type GitCommitResult struct {
	HasChanges   bool
	CommitSHA    string
	FilesChanged []string
	Pushed       bool
	Error        error
}

// PodGitCommitter abstracts git commit/push operations in pods.
// This avoids importing internal/worker directly, preventing import cycles.
type PodGitCommitter interface {
	CommitAndPush(ctx context.Context, podName, workDir, message string, push bool) *GitCommitResult
}

// SDLCOrchestratorService orchestrates SDLC actions: execute, resolve, commit.
type SDLCOrchestratorService struct {
	sdlcService   *SDLCService
	agentRegistry port.CodeAgentRegistry
	gitCommitter  PodGitCommitter
	projectRepo   port.ProjectRepository
}

// NewSDLCOrchestratorService creates a new orchestrator service.
func NewSDLCOrchestratorService(
	sdlcService *SDLCService,
	agentRegistry port.CodeAgentRegistry,
	gitCommitter PodGitCommitter,
	projectRepo port.ProjectRepository,
) *SDLCOrchestratorService {
	return &SDLCOrchestratorService{
		sdlcService:   sdlcService,
		agentRegistry: agentRegistry,
		gitCommitter:  gitCommitter,
		projectRepo:   projectRepo,
	}
}

// ExecuteRequest describes what action to execute.
type ExecuteRequest struct {
	Feature  string `json:"feature"`
	Provider string `json:"provider,omitempty"`
}

// ExecutionResult is the result of executing an SDLC action.
type ExecutionResult struct {
	Action  sdlc.ActionType      `json:"action"`
	Success bool                 `json:"success"`
	Output  string               `json:"output,omitempty"`
	Next    *sdlc.Classification `json:"next,omitempty"`
	Error   string               `json:"error,omitempty"`
}

// ResolveRequest describes a blocker resolution.
type ResolveRequest struct {
	Feature string `json:"feature"`
}

// CommitRequest describes a commit operation.
type CommitRequest struct {
	Feature string `json:"feature"`
	Message string `json:"message"`
	Push    bool   `json:"push"`
}

// CommitResult is the result of a commit operation.
type CommitResult struct {
	CommitSHA    string   `json:"commit_sha,omitempty"`
	FilesChanged []string `json:"files_changed,omitempty"`
	Pushed       bool     `json:"pushed"`
}

// ExecuteAction gets the next classifier action and executes it.
func (s *SDLCOrchestratorService) ExecuteAction(ctx context.Context, projectID string, req *ExecuteRequest) (*ExecutionResult, error) {
	cl, err := s.sdlcService.GetNext(ctx, projectID, req.Feature)
	if err != nil {
		return nil, err
	}

	result := &ExecutionResult{
		Action: cl.Action,
	}

	switch cl.Action {
	case sdlc.ActionTransition:
		err = s.executeTransition(ctx, projectID, cl)
	case sdlc.ActionIdle, sdlc.ActionBlocked, sdlc.ActionAwaitApproval:
		result.Output = cl.Message
		result.Success = true
	default:
		err = s.executeAgentAction(ctx, projectID, cl, req, result)
	}

	if err != nil {
		result.Error = err.Error()
		return result, nil
	}

	if result.Error == "" {
		result.Success = true
	}

	next, nextErr := s.sdlcService.GetNext(ctx, projectID, req.Feature)
	if nextErr == nil {
		result.Next = next
	}

	return result, nil
}

func (s *SDLCOrchestratorService) executeTransition(ctx context.Context, projectID string, cl *sdlc.Classification) error {
	log := logging.FromContext(ctx).WithService("sdlc_orchestrator")
	log.Info("executing transition",
		logging.FieldProjectID, projectID,
		"feature", cl.Feature,
		"to_phase", string(cl.TransitionTo),
	)
	return s.sdlcService.TransitionFeature(ctx, projectID, cl.Feature, cl.TransitionTo)
}

func (s *SDLCOrchestratorService) executeAgentAction(ctx context.Context, projectID string, cl *sdlc.Classification, req *ExecuteRequest, result *ExecutionResult) error {
	var agent port.CodeAgent
	if req.Provider != "" {
		provider, err := domain.ParseAgentProvider(req.Provider)
		if err != nil {
			return fmt.Errorf("invalid agent provider: %w", err)
		}
		agent = s.agentRegistry.Get(provider)
	} else {
		agent = s.agentRegistry.Default()
	}

	if agent == nil {
		return fmt.Errorf("no agent available")
	}

	project, err := s.projectRepo.Get(ctx, domain.ProjectID(projectID))
	if err != nil {
		return fmt.Errorf("resolve project: %w", err)
	}

	prompt := cl.NextCommand
	if prompt == "" {
		prompt = fmt.Sprintf("Execute SDLC action: %s for feature %s", cl.Action, cl.Feature)
	}

	agentReq := &domain.AgentRequest{
		Prompt:    prompt,
		ProjectID: project.ID,
		Timeout:   10 * time.Minute,
		Metadata: map[string]string{
			"pod_name":  project.PodName,
			"namespace": "rdev",
			"action":    string(cl.Action),
			"feature":   cl.Feature,
		},
	}

	log := logging.FromContext(ctx).WithService("sdlc_orchestrator")
	log.Info("dispatching agent action",
		logging.FieldProjectID, projectID,
		"feature", cl.Feature,
		"action", string(cl.Action),
		"agent", agent.Name(),
	)

	var output string
	_, err = agent.Execute(ctx, agentReq, func(event domain.AgentEvent) {
		if event.Type == domain.AgentEventOutput {
			output += event.Content
		}
	})
	if err != nil {
		return fmt.Errorf("agent execution failed: %w", err)
	}

	result.Output = output
	return nil
}

// ResolveBlocker unblocks a feature and re-classifies.
func (s *SDLCOrchestratorService) ResolveBlocker(ctx context.Context, projectID string, req *ResolveRequest) (*ExecutionResult, error) {
	if err := s.sdlcService.UnblockFeature(ctx, projectID, req.Feature); err != nil {
		return nil, err
	}

	log := logging.FromContext(ctx).WithService("sdlc_orchestrator")
	log.Info("blocker resolved", logging.FieldProjectID, projectID, "feature", req.Feature)

	cl, err := s.sdlcService.GetNext(ctx, projectID, req.Feature)
	if err != nil {
		return &ExecutionResult{
			Action:  sdlc.ActionTransition,
			Success: true,
			Output:  "Feature unblocked",
		}, nil
	}

	return &ExecutionResult{
		Action:  sdlc.ActionTransition,
		Success: true,
		Output:  "Feature unblocked",
		Next:    cl,
	}, nil
}

// CommitChanges commits and optionally pushes changes in the project pod.
func (s *SDLCOrchestratorService) CommitChanges(ctx context.Context, projectID string, req *CommitRequest) (*CommitResult, error) {
	if s.gitCommitter == nil {
		return nil, fmt.Errorf("git operations not configured")
	}

	project, err := s.projectRepo.Get(ctx, domain.ProjectID(projectID))
	if err != nil {
		return nil, domain.ErrProjectNotFound
	}

	workDir := "/workspace"
	if project.Workspace != "" {
		workDir = project.Workspace
	}

	gitResult := s.gitCommitter.CommitAndPush(ctx, project.PodName, workDir, req.Message, req.Push)
	if gitResult.Error != nil {
		return nil, gitResult.Error
	}

	log := logging.FromContext(ctx).WithService("sdlc_orchestrator")
	log.Info("changes committed",
		logging.FieldProjectID, projectID,
		"sha", gitResult.CommitSHA,
		"files", len(gitResult.FilesChanged),
		"pushed", gitResult.Pushed,
	)

	return &CommitResult{
		CommitSHA:    gitResult.CommitSHA,
		FilesChanged: gitResult.FilesChanged,
		Pushed:       gitResult.Pushed,
	}, nil
}
