package commands

import (
	"fmt"

	"github.com/sirupsen/logrus"
	"github.com/urfave/cli/v2"

	"gitlab.com/gitlab-org/release-cli/internal/flags"
	"gitlab.com/gitlab-org/release-cli/internal/gitlab"
)

// Update defines the update command to be used by the CLI
func Update(log logrus.FieldLogger, httpClientFn httpClientFn) *cli.Command {
	return &cli.Command{
		Name:  "update",
		Usage: "Update a release using GitLab's Releases API https://docs.gitlab.com/ee/api/releases/#update-a-release",
		Action: func(ctx *cli.Context) error {
			client, err := httpClientFn(ctx, log)
			if err != nil {
				return err
			}

			return updateRelease(ctx, log, client)
		},
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:     flags.TagName,
				Usage:    "The Git tag the release is associated with",
				Required: true,
				EnvVars:  []string{"CI_COMMIT_TAG"},
			},
			&cli.StringFlag{
				Name:     flags.Name,
				Usage:    "The release name",
				Required: false,
			},
			&cli.StringFlag{
				Name:     flags.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,
			},
			&cli.StringSliceFlag{
				Name:     flags.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. Pass an empty string to remove all milestones from the release.`,
				Required: false,
			},
			&cli.StringFlag{
				Name:     flags.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,
			},
		},
	}
}

func updateRelease(ctx *cli.Context, log logrus.FieldLogger, httpClient gitlab.HTTPClient) error {
	projectID := ctx.String(flags.ProjectID)
	serverURL := ctx.String(flags.ServerURL)
	jobToken := ctx.String(flags.JobToken)
	privateToken := ctx.String(flags.PrivateToken)

	l := log.WithFields(logrus.Fields{
		"command":       ctx.Command.Name,
		flags.ServerURL: serverURL,
		flags.ProjectID: projectID,
		flags.Name:      ctx.String(flags.Name),
		flags.TagName:   ctx.String(flags.TagName),
	})

	l.Info("Updating Release...")

	gitlabClient, err := gitlab.New(serverURL, jobToken, privateToken, projectID, httpClient, log)
	if err != nil {
		return fmt.Errorf("create GitLab client: %w", err)
	}

	crr, err := newUpdateReleaseReq(ctx, log)
	if err != nil {
		return fmt.Errorf("new UpdateReleaseRequest: %w", err)
	}

	release, err := gitlabClient.UpdateRelease(ctx.Context, crr)
	if err != nil {
		return fmt.Errorf("update release: %w", err)
	}

	printReleaseOutput(ctx.App.Writer, release, log)

	l.Info("Release updated successfully!")

	return nil
}

func newUpdateReleaseReq(ctx *cli.Context, log logrus.FieldLogger) (*gitlab.UpdateReleaseRequest, error) {
	description := ctx.String(flags.Description)
	releasedAt := ctx.String(flags.ReleasedAt)

	descriptionString, err := getDescription(description, log)
	if err != nil {
		return nil, err
	}

	request := &gitlab.UpdateReleaseRequest{
		ID:          ctx.String(flags.ProjectID),
		Name:        ctx.String(flags.Name),
		Description: descriptionString,
		TagName:     ctx.String(flags.TagName),
		Milestones:  ctx.StringSlice(flags.Milestone),
	}

	if releasedAt != "" {
		timeReleasedAt, err := gitlab.ParseDateTime(releasedAt)
		if err != nil {
			return nil, fmt.Errorf("parse released-at: %w", err)
		}

		request.ReleasedAt = &timeReleasedAt
	}

	return request, nil
}
