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

package task

import (
	stdctx "context"
	"fmt"
	"time"

	gitea "gitea.dev/sdk"

	"gitea.dev/tea/modules/context"
)

// EditIssueOption wraps around gitea.EditIssueOption which has bad & incosistent semantics.
type EditIssueOption struct {
	Index           int64
	Title           *string
	Body            *string
	Ref             *string
	Milestone       *string
	Deadline        *time.Time
	AddLabels       []string
	RemoveLabels    []string
	AddAssignees    []string
	AddReviewers    []string
	RemoveReviewers []string
	// RemoveAssignees []string // NOTE: with the current go-sdk, clearing assignees is not possible.
}

// Normalizes the options into parameters that can be passed to the sdk.
// the returned value will be nil, when no change to this part of the issue is requested.
func (o EditIssueOption) toSdkOptions(requestCtx stdctx.Context, ctx *context.TeaContext, client *gitea.Client) (*gitea.EditIssueOption, *gitea.IssueLabelsOption, *gitea.IssueLabelsOption, error) {
	addLabelOpts, err := ResolveLabelOpts(requestCtx, client, ctx.Owner, ctx.Repo, o.AddLabels)
	if err != nil {
		return nil, nil, nil, err
	}
	rmLabelOpts, err := ResolveLabelOpts(requestCtx, client, ctx.Owner, ctx.Repo, o.RemoveLabels)
	if err != nil {
		return nil, nil, nil, err
	}

	issueOpts := gitea.EditIssueOption{}
	var issueOptsDirty bool
	if o.Title != nil {
		issueOpts.Title = *o.Title
		issueOptsDirty = true
	}
	if o.Body != nil {
		issueOpts.Body = o.Body
		issueOptsDirty = true
	}
	if o.Ref != nil {
		issueOpts.Ref = o.Ref
		issueOptsDirty = true
	}
	if o.Milestone != nil {
		id, err := ResolveMilestoneID(requestCtx, client, ctx.Owner, ctx.Repo, *o.Milestone)
		if err != nil {
			return nil, nil, nil, err
		}
		issueOpts.Milestone = gitea.OptionalInt64(id)
		issueOptsDirty = true
	}
	if o.Deadline != nil {
		issueOpts.Deadline = o.Deadline
		issueOptsDirty = true
		if o.Deadline.IsZero() {
			issueOpts.RemoveDeadline = gitea.OptionalBool(true)
		}
	}
	if len(o.AddAssignees) != 0 {
		issueOpts.Assignees = o.AddAssignees
		issueOptsDirty = true
	}

	if issueOptsDirty {
		return &issueOpts, addLabelOpts, rmLabelOpts, nil
	}
	return nil, addLabelOpts, rmLabelOpts, nil
}

// EditIssue edits an issue and returns the updated issue.
func EditIssue(requestCtx stdctx.Context, ctx *context.TeaContext, client *gitea.Client, opts EditIssueOption) (*gitea.Issue, error) {
	if client == nil {
		client = ctx.Login.Client()
	}

	issueOpts, addLabelOpts, rmLabelOpts, err := opts.toSdkOptions(requestCtx, ctx, client)
	if err != nil {
		return nil, err
	}

	if err := ApplyLabelChanges(requestCtx, client, ctx.Owner, ctx.Repo, opts.Index, addLabelOpts, rmLabelOpts); err != nil {
		return nil, err
	}

	var issue *gitea.Issue
	if issueOpts != nil {
		issue, _, err = client.Issues.EditIssue(requestCtx, ctx.Owner, ctx.Repo, opts.Index, *issueOpts)
		if err != nil {
			return nil, fmt.Errorf("could not edit issue: %s", err)
		}
	} else {
		issue, _, err = client.Issues.GetIssue(requestCtx, ctx.Owner, ctx.Repo, opts.Index)
		if err != nil {
			return nil, fmt.Errorf("could not get issue: %s", err)
		}
	}
	return issue, nil
}
