package pipeline

import "fmt"

func validateResolvedOptions(resolved ResolvedPipeline, catalog ModuleCatalog, configuredLaneIDs []string) error {
	if err := catalog.Inputs.ValidateOptions(resolved.Input.Module, resolved.Input.Options); err != nil {
		return moduleOptionsError(resolved.ID, "", StageInput, resolved.Input.Module, err)
	}
	if err := catalog.Chunkers.ValidateOptions(resolved.Chunk.Module, resolved.Chunk.Options); err != nil {
		return moduleOptionsError(resolved.ID, "", StageChunk, resolved.Chunk.Module, err)
	}
	if err := validateChainOptions(resolved, catalog, StageChunk, "", resolved.Chunk.Module); err != nil {
		return err
	}

	validateLane := func(lane ResolvedArtifactLane) error {
		if err := catalog.Extractors.validateOptions(lane.Extract.Module, lane.Extract.Options); err != nil {
			return moduleOptionsError(resolved.ID, lane.ID, StageExtract, lane.Extract.Module, err)
		}
		if err := validateChainOptions(resolved, catalog, StageExtract, lane.ID, lane.Extract.Module); err != nil {
			return err
		}
		if err := catalog.Mergers.validateOptions(lane.Merge.Module, lane.ArtifactKind, lane.Merge.Options); err != nil {
			return moduleOptionsError(resolved.ID, lane.ID, StageMerge, lane.Merge.Module, err)
		}
		if err := validateChainOptions(resolved, catalog, StageMerge, lane.ID, lane.Merge.Module); err != nil {
			return err
		}
		if err := catalog.Normalizers.validateOptions(lane.Normalize.Module, lane.ArtifactKind, lane.Normalize.Options); err != nil {
			return moduleOptionsError(resolved.ID, lane.ID, StageNormalize, lane.Normalize.Module, err)
		}
		return validateChainOptions(resolved, catalog, StageNormalize, lane.ID, lane.Normalize.Module)
	}
	for _, step := range resolved.Steps {
		for _, lane := range step.ArtifactLanes {
			if err := validateLane(lane); err != nil {
				return err
			}
		}
	}

	if err := catalog.Outputs.ValidateOptions(resolved.Output.Module, resolved.Output.Options); err != nil {
		return moduleOptionsError(resolved.ID, "", StageOutput, resolved.Output.Module, err)
	}
	if err := catalog.Outputs.ValidateProfileOptions(resolved.Output.Module, OutputProfileOptionContext{LaneIDs: configuredLaneIDs}, resolved.Output.Options); err != nil {
		return moduleOptionsError(resolved.ID, "", StageOutput, resolved.Output.Module, err)
	}
	return nil
}

func validateChainOptions(resolved ResolvedPipeline, catalog ModuleCatalog, stage ModuleStage, laneID, moduleKey string) error {
	chain := resolvedValidatorChain(stage, laneID, moduleKey, resolved.ValidatorChains)
	for _, validator := range chain.Validators {
		if err := catalog.Validators.validateOptions(validator); err != nil {
			return validatorOptionsError(resolved.ID, laneID, stage, moduleKey, validator.Binding.Module, err)
		}
	}
	return nil
}

func moduleOptionsError(pipelineID, laneID string, stage ModuleStage, moduleKey string, cause error) error {
	if laneID == "" {
		return fmt.Errorf("pipeline %q %s module %q options: %w", pipelineID, stage, moduleKey, cause)
	}
	return fmt.Errorf("pipeline %q lane %q %s module %q options: %w", pipelineID, laneID, stage, moduleKey, cause)
}

func validatorOptionsError(pipelineID, laneID string, stage ModuleStage, moduleKey, validatorKey string, cause error) error {
	if laneID == "" {
		return fmt.Errorf("pipeline %q %s module %q validator %q options: %w", pipelineID, stage, moduleKey, validatorKey, cause)
	}
	return fmt.Errorf("pipeline %q lane %q %s module %q validator %q options: %w", pipelineID, laneID, stage, moduleKey, validatorKey, cause)
}
