package msg

import (
	"fmt"
	"strings"
	"time"

	"git.sr.ht/~rjarry/aerc/app"
	"git.sr.ht/~rjarry/aerc/commands"
	"git.sr.ht/~rjarry/aerc/models"
	"git.sr.ht/~rjarry/aerc/worker/types"
)

type FlagMsg struct {
	Toggle    bool         `opt:"-t" desc:"Toggle between set and unset."`
	Answered  bool         `opt:"-a" aliases:"flag,unflag" desc:"Set/unset the answered flag."`
	Forwarded bool         `opt:"-f" aliases:"flag,unflag" desc:"Set/unset the forwarded flag."`
	Flag      models.Flags `opt:"-x" aliases:"flag,unflag" action:"ParseFlag" complete:"CompleteFlag" desc:"Flag name."`
	FlagName  string
}

func init() {
	commands.Register(FlagMsg{})
}

func (FlagMsg) Description() string {
	return "Set or unset a flag on the marked or selected messages."
}

func (FlagMsg) Context() commands.CommandContext {
	return commands.MESSAGE_LIST | commands.MESSAGE_VIEWER
}

func (FlagMsg) Aliases() []string {
	return []string{"flag", "unflag", "read", "unread"}
}

func (f *FlagMsg) ParseFlag(arg string) error {
	switch strings.ToLower(arg) {
	case "seen":
		f.Flag = models.SeenFlag
		f.FlagName = "seen"
	case "answered":
		f.Flag = models.AnsweredFlag
		f.FlagName = "answered"
	case "forwarded":
		f.Flag = models.ForwardedFlag
		f.FlagName = "forwarded"
	case "flagged":
		f.Flag = models.FlaggedFlag
		f.FlagName = "flagged"
	case "draft":
		f.Flag = models.DraftFlag
		f.FlagName = "draft"
	default:
		return fmt.Errorf("Unknown flag %q", arg)
	}
	return nil
}

var validFlags = []string{"seen", "answered", "forwarded", "flagged", "draft"}

func (*FlagMsg) CompleteFlag(arg string) []string {
	return commands.FilterList(validFlags, arg, nil)
}

// If this was called as 'flag' or 'unflag', without the toggle (-t)
// option, then it will flag the corresponding messages with the given
// flag.  If the toggle option was given, it will individually toggle
// the given flag for the corresponding messages.
//
// If this was called as 'read' or 'unread', it has the same effect as
// 'flag' or 'unflag', respectively, but the 'Seen' flag is affected.
func (f FlagMsg) Execute(args []string) error {
	// User-readable name for the action being performed
	var actionName string

	switch args[0] {
	case "read", "unread":
		f.Flag = models.SeenFlag
		f.FlagName = "seen"
	case "flag", "unflag":
		if f.Answered {
			f.Flag = models.AnsweredFlag
			f.FlagName = "answered"
		}
		if f.Forwarded {
			f.Flag = models.ForwardedFlag
			f.FlagName = "forwarded"
		}
		if f.Flag == 0 {
			f.Flag = models.FlaggedFlag
			f.FlagName = "flagged"
		}
	}

	h := newHelper()
	store, err := h.store()
	if err != nil {
		return err
	}

	// UIDs of messages to enable or disable the flag for.
	var toEnable []models.UID
	var toDisable []models.UID

	if f.Toggle {
		// If toggling, split messages into those that need to
		// be enabled / disabled.
		msgs, err := h.messages()
		if err != nil {
			return err
		}
		for _, m := range msgs {
			if m.Flags.Has(f.Flag) {
				toDisable = append(toDisable, m.Uid)
			} else {
				toEnable = append(toEnable, m.Uid)
			}
		}
		actionName = "Toggling"
	} else {
		msgUids, err := h.markedOrSelectedUids()
		if err != nil {
			return err
		}
		switch args[0] {
		case "read", "flag":
			toEnable = msgUids
			actionName = "Setting"
		default:
			toDisable = msgUids
			actionName = "Unsetting"
		}
	}

	status := fmt.Sprintf("%s flag %q successful", actionName, f.FlagName)

	if len(toEnable) != 0 {
		store.Flag(toEnable, f.Flag, true, func(msg types.WorkerMessage) {
			switch msg := msg.(type) {
			case *types.Done:
				app.PushStatus(status, 10*time.Second)
				store.Marker().ClearVisualMark()
			case *types.Error:
				app.PushError(msg.Error.Error())
			}
		})
	}
	if len(toDisable) != 0 {
		store.Flag(toDisable, f.Flag, false, func(msg types.WorkerMessage) {
			switch msg := msg.(type) {
			case *types.Done:
				app.PushStatus(status, 10*time.Second)
				store.Marker().ClearVisualMark()
			case *types.Error:
				app.PushError(msg.Error.Error())
			}
		})
	}
	return nil
}
