package config

import (
	"fmt"
	"strings"

	"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
)

type ResolveInput struct {
	PipelineID         string
	Only               []string
	Catalog            pipeline.ModuleCatalog
	LLMProfileOverride string
	ReferenceOverrides []pipeline.ReferenceBinding
	ReferenceUnbinds   []pipeline.ReferenceUnbind
}

type EffectiveConfig struct {
	Config             Config
	PipelineID         string
	Only               []string
	ReferenceOverrides []pipeline.ReferenceBinding
	ReferenceUnbinds   []pipeline.ReferenceUnbind
	ResolvedPipeline   pipeline.ResolvedPipeline
}

func (c Config) Resolve(input ResolveInput) (EffectiveConfig, error) {
	c.Concurrency.recomputeStageWorkerDefaults()
	if err := c.Validate(); err != nil {
		return EffectiveConfig{}, err
	}

	pipelineID := strings.TrimSpace(input.PipelineID)
	if pipelineID == "" {
		return EffectiveConfig{}, fmt.Errorf("pipeline id must not be empty")
	}

	profile, ok := lookupPipelineProfile(c.Pipelines, pipelineID)
	if !ok {
		return EffectiveConfig{}, fmt.Errorf("pipeline %q is not configured", pipelineID)
	}
	profile = clonePipelineProfile(profile)
	profile.ID = pipelineID

	resolved, err := pipeline.ResolvePipeline(profile, pipeline.ResolveOptions{
		Only:               input.Only,
		LLMProfileOverride: input.LLMProfileOverride,
		ReferenceOverrides: append([]pipeline.ReferenceBinding(nil), input.ReferenceOverrides...),
		ReferenceUnbinds:   append([]pipeline.ReferenceUnbind(nil), input.ReferenceUnbinds...),
	}, input.Catalog)
	if err != nil {
		return EffectiveConfig{}, fmt.Errorf("resolve pipeline %q: %w", pipelineID, err)
	}

	return EffectiveConfig{
		Config:             cloneConfig(c),
		PipelineID:         pipelineID,
		Only:               append([]string(nil), input.Only...),
		ReferenceOverrides: append([]pipeline.ReferenceBinding(nil), input.ReferenceOverrides...),
		ReferenceUnbinds:   append([]pipeline.ReferenceUnbind(nil), input.ReferenceUnbinds...),
		ResolvedPipeline:   resolved,
	}, nil
}

func lookupPipelineProfile(profiles map[string]pipeline.PipelineProfile, pipelineID string) (pipeline.PipelineProfile, bool) {
	pipelineID = strings.TrimSpace(pipelineID)
	for rawID, profile := range profiles {
		if strings.TrimSpace(rawID) == pipelineID {
			return profile, true
		}
	}
	return pipeline.PipelineProfile{}, false
}
