package flags

import (
	"github.com/urfave/cli/v2"
	"github.com/urfave/cli/v2/altsrc"
)

// PassedInFlags abstracts flags and before hook to specify in cli.Command,
// when creating a new release either with a file or with parameters.
type PassedInFlags interface {
	ListFlags() *[]cli.Flag
	BeforeHook([]cli.Flag) cli.BeforeFunc
}

// ParameterFlag is a struct used when instantiating PassedInFlags upon passing release data as parameters.
type ParameterFlag struct{}

// FileFlag is a struct used when instantiating PassedInFlags upon passing a file as parameter.
type FileFlag struct{}

// This is the BeforeHook implementation for ParameterFlag.
func (ParameterFlag) BeforeHook(flags []cli.Flag) cli.BeforeFunc { return nil }

// This is the BeforeHook implementation for FileFlag.
func (FileFlag) BeforeHook(flags []cli.Flag) cli.BeforeFunc {
	return altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc(File))
}

// ListFlags implementation for ParameterFlag.
func (ParameterFlag) ListFlags() *[]cli.Flag {
	return &[]cli.Flag{
		tagNameFlag(true, false),
		nameFlag(false),
		descriptionFlag(false),
		tagMessageFlag(false),
		refFlag(false),
		assetsLinkFlag(false),
		milestoneFlag(false),
		releasedAtFlag(false),
		catalogPublishFlag(false),
	}
}

// ListFlags implementation for FileFlag.
func (FileFlag) ListFlags() *[]cli.Flag {
	return &[]cli.Flag{
		altsrc.NewStringFlag(tagNameFlag(false, true)),
		altsrc.NewStringFlag(nameFlag(true)),
		altsrc.NewStringFlag(descriptionFlag(true)),
		altsrc.NewStringFlag(tagMessageFlag(true)),
		altsrc.NewStringFlag(refFlag(true)),
		altsrc.NewStringSliceFlag(assetsLinkFlag(true)),
		altsrc.NewStringSliceFlag(milestoneFlag(true)),
		altsrc.NewStringFlag(releasedAtFlag(true)),
		altsrc.NewBoolFlag(catalogPublishFlag(true)),
		&cli.StringFlag{
			Name:     File,
			Usage:    `YML file which content holds data to create a release. Does not need use of any other parameters.`,
			Required: true,
		},
	}
}

func nameFlag(hidden bool) *cli.StringFlag {
	return &cli.StringFlag{
		Name:     Name,
		Usage:    "The release name",
		Required: false,
		Hidden:   hidden,
	}
}

func descriptionFlag(hidden bool) *cli.StringFlag {
	return &cli.StringFlag{
		Name:     Description,
		Usage:    "The description of the release; you can use Markdown. A file can be used to read the description contents, must exist inside the working directory; if it contains any whitespace, it will be treated as a string",
		Required: false,
		Hidden:   hidden,
	}
}

func tagNameFlag(required bool, hidden bool) *cli.StringFlag {
	return &cli.StringFlag{
		Name:     TagName,
		Usage:    "(required) The tag the release will be created from",
		Required: required,
		EnvVars:  []string{"CI_COMMIT_TAG"},
		Hidden:   hidden,
	}
}

func tagMessageFlag(hidden bool) *cli.StringFlag {
	return &cli.StringFlag{
		Name:     TagMessage,
		Usage:    "Message to use if creating a new annotated tag",
		Required: false,
		Hidden:   hidden,
	}
}

func refFlag(hidden bool) *cli.StringFlag {
	return &cli.StringFlag{
		Name:     Ref,
		Usage:    "If tag_name doesn’t exist, the release will be created from ref; it can be a commit SHA, another tag name, or a branch name",
		Required: false,
		EnvVars:  []string{"CI_COMMIT_SHA"},
		Hidden:   hidden,
	}
}

func assetsLinkFlag(hidden bool) *cli.StringSliceFlag {
	return &cli.StringSliceFlag{
		Name:     AssetsLink,
		Usage:    `JSON string representation of an asset link; (e.g. --assets-link='{"name": "Asset1", "url":"https://<domain>/some/location/1", "type": "other", "direct_asset_path": "xzy" }' or --assets-link='[{"name": "Asset1", "url":"https://example.com/some/location/1"}, {"name": "Asset2", "url":"https://example.com/some/location/2"}]'. The filepath field is deprecated and aliased to the direct_asset_path field. If both are set the direct_asset_path takes precedence.`,
		Required: false,
		Hidden:   hidden,
	}
}

func milestoneFlag(hidden bool) *cli.StringSliceFlag {
	return &cli.StringSliceFlag{
		Name:     Milestone,
		Usage:    `List of the titles of each milestone the release is associated with (e.g. --milestone "v1.0" --milestone "v1.0-rc)"; each milestone needs to exist `,
		Required: false,
		Hidden:   hidden,
	}
}

func releasedAtFlag(hidden bool) *cli.StringFlag {
	return &cli.StringFlag{
		Name:     ReleasedAt,
		Usage:    `The date when the release will be/was ready; defaults to the current time; expected in ISO 8601 format (2019-03-15T08:00:00Z)`,
		Required: false,
		Hidden:   hidden,
	}
}

func catalogPublishFlag(hidden bool) *cli.BoolFlag {
	return &cli.BoolFlag{
		Name:     CatalogPublish,
		Usage:    `Set to true if you want to publish the catalog resource release to the CI/CD catalog`,
		Required: false,
		Hidden:   hidden,
	}
}
