package gitlab

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/hashicorp/go-multierror"
)

// ConflictDirectAssetPathError is returned when both direct_asset_path and the deprecated filepath as specified for an asset link.
var ConflictDirectAssetPathError = fmt.Errorf("asset link can either specify `direct_asset_path` or `filepath` (`filepath` is deprecated).")

// AssetMarshaller parses asset links, either single instance of them or as an array.
func AssetMarshaller(assetLink []string) (*Assets, error) {
	assets := &Assets{
		Links: []*Link{},
	}

	if len(assetLink) == 0 {
		return assets, nil
	}

	var result *multierror.Error

	for _, asset := range assetLink {
		dec := json.NewDecoder(strings.NewReader(asset))
		t, _ := dec.Token()

		var err error

		switch t {
		case json.Delim('['):
			err = processMultipleAssets(asset, assets)
		case json.Delim('{'):
			err = processSingleAsset(asset, assets)
		default:
			err = fmt.Errorf("invalid delimiter for asset: %q", asset)
		}

		if err != nil {
			result = multierror.Append(result, err)
		}
	}

	return assets, result.ErrorOrNil()
}

// processSingleAsset parses a single asset link object.
func processSingleAsset(entry string, assets *Assets) error {
	var link Link

	if err := json.Unmarshal([]byte(entry), &link); err != nil {
		return fmt.Errorf("invalid asset: %q %w", entry, err)
	}

	if err := ensureUsingDirectAssetPath(&link); err != nil {
		return fmt.Errorf("invalid asset: %q %w", entry, err)
	}

	assets.Links = append(assets.Links, &link)

	return nil
}

// processMultipleAssets parses an array of asset link objects.
func processMultipleAssets(entry string, assets *Assets) error {
	var links []*Link

	if err := json.Unmarshal([]byte(entry), &links); err != nil {
		return fmt.Errorf("invalid array of assets: %q %w", entry, err)
	}

	for _, link := range links {
		if err := ensureUsingDirectAssetPath(link); err != nil {
			return fmt.Errorf("invalid array of assets: %q %w", entry, err)
		}
	}

	assets.Links = append(assets.Links, links...)

	return nil
}

func ensureUsingDirectAssetPath(link *Link) error {
	if link.Filepath == "" {
		// There is no deprecated filepath set, so we are good.
		return nil
	}

	if link.DirectAssetPath != "" {
		// Both, filepath and direct_asset_path are set, so we have a conflict
		return ConflictDirectAssetPathError
	}

	// We use the set filepath as direct_asset_path
	// and clear the filepath to net send it via API.
	link.DirectAssetPath = link.Filepath
	link.Filepath = ""

	return nil
}
