package exec

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/cloudposse/atmos/pkg/perf"

	log "github.com/cloudposse/atmos/pkg/logger"

	errUtils "github.com/cloudposse/atmos/errors"
	cfg "github.com/cloudposse/atmos/pkg/config"
	"github.com/cloudposse/atmos/pkg/schema"
	u "github.com/cloudposse/atmos/pkg/utils"
)

// ExecutePackerOutput executes `atmos packer output` commands.
func ExecutePackerOutput(
	info *schema.ConfigAndStacksInfo,
	packerFlags *PackerFlags,
) (any, error) {
	defer perf.Track(nil, "exec.ExecutePackerOutput")()

	atmosConfig, err := cfg.InitCliConfig(*info, true)
	if err != nil {
		return nil, err
	}

	*info, err = ProcessStacks(&atmosConfig, *info, true, true, true, nil)
	if err != nil {
		return nil, err
	}

	if len(info.Stack) < 1 {
		return nil, errUtils.ErrMissingStack
	}

	if !info.ComponentIsEnabled {
		log.Info("Component is not enabled and skipped", "component", info.ComponentFromArg)
		return nil, nil
	}

	// Check if the component exists as a Packer component.
	componentPath, err := u.GetComponentPath(&atmosConfig, "packer", info.ComponentFolderPrefix, info.FinalComponent)
	if err != nil {
		return nil, fmt.Errorf("failed to resolve component path: %w", err)
	}

	componentPathExists, err := u.IsDirectory(componentPath)
	if err != nil || !componentPathExists {
		// Get the base path for error message, respecting user's actual config
		basePath, _ := u.GetComponentBasePath(&atmosConfig, "packer")
		return nil, fmt.Errorf("%w: Atmos component `%s` points to the Packer component `%s`, but it does not exist in `%s`",
			errUtils.ErrInvalidComponent,
			info.ComponentFromArg,
			info.FinalComponent,
			basePath,
		)
	}

	// Find Packer manifest filename.
	manifestFilename, err := GetPackerManifestFromVars(&info.ComponentVarsSection)
	if err != nil {
		return nil, err
	}

	if manifestFilename == "" {
		manifestFilename = "manifest.json"
	}

	manifestPath := filepath.Join(componentPath, manifestFilename)
	if !u.FileExists(manifestPath) {
		return nil, fmt.Errorf("%w: `%s`.\nIt's generated by Packer when executing `atmos packer build`.\nThe manifest filename is specified in the `manifest_file_name` variable of the Atmos component.",
			errUtils.ErrMissingPackerManifest,
			filepath.Join(atmosConfig.Components.Packer.BasePath, info.ComponentFolderPrefix, info.FinalComponent, manifestFilename),
		)
	}

	manifestContent, err := os.ReadFile(manifestPath)
	if err != nil {
		return nil, errors.Join(errUtils.ErrReadFile, err)
	}

	data, err := u.ConvertFromJSON(string(manifestContent))
	if err != nil {
		return nil, err
	}

	if packerFlags.Query != "" {
		data, err = u.EvaluateYqExpression(&atmosConfig, data, packerFlags.Query)
		if err != nil {
			return nil, err
		}
	}

	return data, nil
}
