package imap

import (
	"fmt"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-imap/client"
)

// MoveMessages moves messages identified by UIDs from the current mailbox
// to the target mailbox. Emails are never deleted, only moved.
func (c *Client) MoveMessages(mailbox string, uids []uint32, target string) error {
	return c.Execute(func(cl *client.Client) error {
		_, err := cl.Select(mailbox, false)
		if err != nil {
			return fmt.Errorf("select source %q: %w", mailbox, err)
		}

		if err := ensureFolder(cl, target); err != nil {
			return fmt.Errorf("ensure target %q: %w", target, err)
		}

		seqset := new(imap.SeqSet)
		seqset.AddNum(uids...)

		if err := cl.UidMove(seqset, target); err != nil {
			return fmt.Errorf("uid move to %q: %w", target, err)
		}

		return nil
	})
}

// CopyMessages copies messages to a target folder without removing from source
func (c *Client) CopyMessages(mailbox string, uids []uint32, target string) error {
	return c.Execute(func(cl *client.Client) error {
		_, err := cl.Select(mailbox, true)
		if err != nil {
			return fmt.Errorf("select source %q: %w", mailbox, err)
		}

		if err := ensureFolder(cl, target); err != nil {
			return fmt.Errorf("ensure target %q: %w", target, err)
		}

		seqset := new(imap.SeqSet)
		seqset.AddNum(uids...)

		if err := cl.UidCopy(seqset, target); err != nil {
			return fmt.Errorf("uid copy to %q: %w", target, err)
		}

		return nil
	})
}

// MarkSeen marks messages as read by UID
func (c *Client) MarkSeen(mailbox string, uids []uint32) error {
	return c.Execute(func(cl *client.Client) error {
		_, err := cl.Select(mailbox, false)
		if err != nil {
			return fmt.Errorf("select %q: %w", mailbox, err)
		}

		seqset := new(imap.SeqSet)
		seqset.AddNum(uids...)

		if err := cl.UidStore(seqset, imap.FormatFlagsOp(imap.AddFlags, false), []interface{}{imap.SeenFlag}, nil); err != nil {
			return fmt.Errorf("mark seen: %w", err)
		}

		return nil
	})
}

// ensureFolder creates a folder if it doesn't exist
func ensureFolder(cl *client.Client, name string) error {
	mailboxes := make(chan *imap.MailboxInfo, 10)
	done := make(chan error, 1)

	go func() {
		done <- cl.List("", "*", mailboxes)
	}()

	existing := make(map[string]bool)
	for m := range mailboxes {
		existing[m.Name] = true
	}

	if err := <-done; err != nil {
		return fmt.Errorf("list: %w", err)
	}

	if !existing[name] {
		if err := cl.Create(name); err != nil {
			return fmt.Errorf("create %q: %w", name, err)
		}
	}

	return nil
}
