package action

import (
	"fmt"
	"strings"
	"time"

	relent "github.com/tidbcloud/devops/entity/release"
	larknotify "github.com/tidbcloud/devops/pkg/infra/clients/larkcli/notify"
	"github.com/tidbcloud/devops/pkg/infra/errs"
)

var (
	resultsToClose = []relent.ChangeResult{
		relent.ChangeResultSuccessful, relent.ChangeResultFailed,
		relent.ChangeResultFailedPartially, relent.ChangeResultRollback,
	}
)

type actionWhenObserving struct {
	*actionBase
}

func (a *actionWhenObserving) NotifyToCloseIfNeeded() error {
	ctx := a.Context
	change := a.Change
	now := time.Now()

	reasons, err := a.canClose(relent.ChangeResultSuccessful)
	if err != nil {
		return fmt.Errorf("can close change failed: %w", err)
	}

	if len(reasons) != 0 {
		return errs.RetriableErrorf("can not close change: %s", strings.Join(reasons, ","))
	}

	notifyInterval := *a.Conf.NotifyToCloseInterval
	lastDeployedAt := change.LastDeployment().DeployedAt
	lastNotifiedAt := change.Notification.NotifiedToCloseAt

	if lastNotifiedAt != nil {
		if now.Sub(*lastNotifiedAt) < notifyInterval {
			return errs.RetriableErrorf("must wait at least %s", a.Conf.MinObserveDuration.String())
		}
	}

	text := fmt.Sprintf("This change has been deployed for %s, please remember to close it.",
		now.Sub(*lastDeployedAt).Truncate(time.Second).String())
	_, err = a.LarkClient.Notify.ReplyMessage(ctx, *change.Notification.LarkTopicID, larknotify.TextMessageParam(text), true)
	if err != nil {
		return err
	}

	return a.PatchChange(ctx,
		func(current *relent.ChangeInRepo) error {
			current.Notification.NotifiedToCloseAt = &now
			return nil
		},
	)

}

func (a *actionWhenObserving) Close(result relent.ChangeResult, summary string) error {
	return a.close(result, summary)
}

func (a *actionWhenObserving) VerifyDeployment(order relent.ChangeDeployOrder, note string) error {
	return a.actionBase.verifyDeployment(order, note)
}

func (a *actionWhenObserving) canClose(result relent.ChangeResult) ([]string, error) {
	change := a.Change
	now := time.Now()

	reason := []string{}

	if result == relent.ChangeResultSuccessful {
		// min observe duration when success
		last := change.LastDeployment()
		if last != nil {
			if last.DeployedAt != nil && now.Sub(*last.DeployedAt) < *a.Conf.MinObserveDuration {
				reason = append(reason, fmt.Sprintf("must observe at least %s", a.Conf.MinObserveDuration.String()))
			}
		}

	}

	return reason, nil
}
