// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package flags

import (
	"fmt"
	"strings"
	"time"

	"code.gitea.io/sdk/gitea"
	"code.gitea.io/tea/modules/context"
	"code.gitea.io/tea/modules/task"

	"github.com/araddon/dateparse"
	"github.com/urfave/cli/v2"
)

// StateFlag provides flag to specify issue/pr state, defaulting to "open"
var StateFlag = cli.StringFlag{
	Name:        "state",
	Usage:       "Filter by state (all|open|closed)",
	DefaultText: "open",
}

// MilestoneFilterFlag is a CSV flag used to filter issues by milestones
var MilestoneFilterFlag = NewCsvFlag(
	"milestones",
	"milestones to match issues against",
	[]string{"m"}, nil, nil)

// LabelFilterFlag is a CSV flag used to filter issues by labels
var LabelFilterFlag = NewCsvFlag(
	"labels",
	"labels to match issues against",
	[]string{"L"}, nil, nil)

// PRListingFlags defines flags that should be available on pr listing flags.
var PRListingFlags = append([]cli.Flag{
	&StateFlag,
	&PaginationPageFlag,
	&PaginationLimitFlag,
}, AllDefaultFlags...)

// IssueListingFlags defines flags that should be available on issue listing flags.
var IssueListingFlags = append([]cli.Flag{
	&StateFlag,
	&cli.StringFlag{
		Name:        "kind",
		Aliases:     []string{"K"},
		Usage:       "Whether to return `issues`, `pulls`, or `all` (you can use this to apply advanced search filters to PRs)",
		DefaultText: "issues",
	},
	&cli.StringFlag{
		Name:    "keyword",
		Aliases: []string{"k"},
		Usage:   "Filter by search string",
	},
	LabelFilterFlag,
	MilestoneFilterFlag,
	&cli.StringFlag{
		Name:    "author",
		Aliases: []string{"A"},
	},
	&cli.StringFlag{
		Name:    "assignee",
		Aliases: []string{"a"},
	},
	&cli.StringFlag{
		Name:    "mentions",
		Aliases: []string{"M"},
	},
	&cli.StringFlag{
		Name:    "owner",
		Aliases: []string{"org"},
	},
	&cli.StringFlag{
		Name:    "from",
		Aliases: []string{"F"},
		Usage:   "Filter by activity after this date",
	},
	&cli.StringFlag{
		Name:    "until",
		Aliases: []string{"u"},
		Usage:   "Filter by activity before this date",
	},
	&PaginationPageFlag,
	&PaginationLimitFlag,
}, AllDefaultFlags...)

// issuePRFlags defines shared flags between flags IssuePRCreateFlags and IssuePREditFlags
var issuePRFlags = append([]cli.Flag{
	&cli.StringFlag{
		Name:    "title",
		Aliases: []string{"t"},
	},
	&cli.StringFlag{
		Name:    "description",
		Aliases: []string{"d"},
	},
	&cli.StringFlag{
		Name:    "referenced-version",
		Aliases: []string{"v"},
		Usage:   "commit-hash or tag name to assign",
	},
	&cli.StringFlag{
		Name:    "milestone",
		Aliases: []string{"m"},
		Usage:   "Milestone to assign",
	},
	&cli.StringFlag{
		Name:    "deadline",
		Aliases: []string{"D"},
		Usage:   "Deadline timestamp to assign",
	},
}, LoginRepoFlags...)

// IssuePRCreateFlags defines flags for creation of issues and PRs
var IssuePRCreateFlags = append([]cli.Flag{
	&cli.StringFlag{
		Name:    "assignees",
		Aliases: []string{"a"},
		Usage:   "Comma-separated list of usernames to assign",
	},
	&cli.StringFlag{
		Name:    "labels",
		Aliases: []string{"L"},
		Usage:   "Comma-separated list of labels to assign",
	},
}, issuePRFlags...)

// GetIssuePRCreateFlags parses all IssuePREditFlags
func GetIssuePRCreateFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
	opts := gitea.CreateIssueOption{
		Title:     ctx.String("title"),
		Body:      ctx.String("description"),
		Assignees: strings.Split(ctx.String("assignees"), ","),
	}
	var err error

	date := ctx.String("deadline")
	if date != "" {
		t, err := dateparse.ParseAny(date)
		if err != nil {
			return nil, err
		}
		opts.Deadline = &t
	}

	client := ctx.Login.Client()

	labelNames := strings.Split(ctx.String("labels"), ",")
	if len(labelNames) != 0 {
		if client == nil {
			client = ctx.Login.Client()
		}
		if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil {
			return nil, err
		}
	}

	if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 {
		if client == nil {
			client = ctx.Login.Client()
		}
		ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName)
		if err != nil {
			return nil, fmt.Errorf("Milestone '%s' not found", milestoneName)
		}
		opts.Milestone = ms.ID
	}

	return &opts, nil
}

// IssuePREditFlags defines flags for editing properties of issues and PRs
var IssuePREditFlags = append([]cli.Flag{
	&cli.StringFlag{
		Name:    "add-assignees",
		Aliases: []string{"a"},
		Usage:   "Comma-separated list of usernames to assign",
	},
	&cli.StringFlag{
		Name:    "add-labels",
		Aliases: []string{"L"},
		Usage:   "Comma-separated list of labels to assign. Takes precedence over --remove-labels",
	},
	&cli.StringFlag{
		Name:  "remove-labels",
		Usage: "Comma-separated list of labels to remove",
	},
}, issuePRFlags...)

// GetIssuePREditFlags parses all IssuePREditFlags
func GetIssuePREditFlags(ctx *context.TeaContext) (*task.EditIssueOption, error) {
	opts := task.EditIssueOption{}
	if ctx.IsSet("title") {
		val := ctx.String("title")
		opts.Title = &val
	}
	if ctx.IsSet("description") {
		val := ctx.String("description")
		opts.Body = &val
	}
	if ctx.IsSet("referenced-version") {
		val := ctx.String("referenced-version")
		opts.Ref = &val
	}
	if ctx.IsSet("milestone") {
		val := ctx.String("milestone")
		opts.Milestone = &val
	}
	if ctx.IsSet("deadline") {
		date := ctx.String("deadline")
		if date == "" {
			opts.Deadline = &time.Time{}
		} else {
			t, err := dateparse.ParseAny(date)
			if err != nil {
				return nil, err
			}
			opts.Deadline = &t
		}
	}
	if ctx.IsSet("add-assignees") {
		val := ctx.String("add-assignees")
		opts.AddAssignees = strings.Split(val, ",")
	}
	if ctx.IsSet("add-labels") {
		val := ctx.String("add-labels")
		opts.AddLabels = strings.Split(val, ",")
	}
	if ctx.IsSet("remove-labels") {
		val := ctx.String("remove-labels")
		opts.RemoveLabels = strings.Split(val, ",")
	}
	return &opts, nil
}