// Package testutil provides testing utilities for rdev-api.
package testutil

import (
	"context"
	"sync"

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

// MockExecutor is a mock implementation of the Executor for testing.
type MockExecutor struct {
	mu              sync.Mutex
	ExecCalls       []ExecCall
	ExecResult      *domain.CommandResult
	ExecOutputs     []domain.OutputLine
	PodExistsMap    map[string]bool
	ConnectionError error
}

// ExecCall records the parameters of an Execute call.
type ExecCall struct {
	Cmd     *domain.Command
	PodName string
}

// Ensure MockExecutor implements port.CommandExecutor at compile time.
var _ port.CommandExecutor = (*MockExecutor)(nil)

// NewMockExecutor creates a new mock executor.
func NewMockExecutor() *MockExecutor {
	return &MockExecutor{
		PodExistsMap: make(map[string]bool),
		ExecResult:   &domain.CommandResult{},
	}
}

// Execute mocks command execution.
func (m *MockExecutor) Execute(ctx context.Context, cmd *domain.Command, podName string, handler domain.OutputHandler) (*domain.CommandResult, error) {
	m.mu.Lock()
	m.ExecCalls = append(m.ExecCalls, ExecCall{Cmd: cmd, PodName: podName})
	outputs := m.ExecOutputs
	result := m.ExecResult
	m.mu.Unlock()

	// Send mock outputs
	for _, o := range outputs {
		select {
		case <-ctx.Done():
			return &domain.CommandResult{CommandID: cmd.ID, ExitCode: 130, Error: ctx.Err()}, nil
		default:
			handler(o)
		}
	}

	return result, nil
}

// Cancel mocks command cancellation.
func (m *MockExecutor) Cancel(ctx context.Context, cmdID domain.CommandID) error {
	return nil
}

// SetExecResult sets the result to return from Execute.
func (m *MockExecutor) SetExecResult(result *domain.CommandResult) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.ExecResult = result
}

// SetExecOutputs sets the outputs to send during Execute.
func (m *MockExecutor) SetExecOutputs(outputs []domain.OutputLine) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.ExecOutputs = outputs
}

// PodExists mocks pod existence check.
func (m *MockExecutor) PodExists(ctx context.Context, podName string) (bool, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	exists, ok := m.PodExistsMap[podName]
	if !ok {
		return false, nil
	}
	return exists, nil
}

// CheckConnection mocks cluster connection check.
func (m *MockExecutor) CheckConnection(ctx context.Context) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.ConnectionError
}

// SetConnectionError sets the error to return from CheckConnection.
func (m *MockExecutor) SetConnectionError(err error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.ConnectionError = err
}

// SetPodExists sets whether a pod exists.
func (m *MockExecutor) SetPodExists(podName string, exists bool) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.PodExistsMap[podName] = exists
}

// GetExecCalls returns all recorded Execute calls.
func (m *MockExecutor) GetExecCalls() []ExecCall {
	m.mu.Lock()
	defer m.mu.Unlock()
	return append([]ExecCall{}, m.ExecCalls...)
}

// Reset clears all recorded calls and resets the mock.
func (m *MockExecutor) Reset() {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.ExecCalls = nil
	m.ExecOutputs = nil
	m.ExecResult = &domain.CommandResult{}
	m.PodExistsMap = make(map[string]bool)
}
