// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.

package cli

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	"github.com/bndr/gotabulate"

	"github.com/la5nta/pat/app"
	"github.com/la5nta/wl2k-go/fbb"
	"github.com/la5nta/wl2k-go/mailbox"
)

var mailboxes = []string{"in", "out", "sent", "archive"}

func ReadHandle(ctx context.Context, app *app.App, _ []string) {
	cancel := exitOnContextCancellation(ctx)
	defer cancel()

	w := os.Stdout

	for {
		// Query user for mailbox to list
		printMailboxes(w)
		fmt.Fprintf(w, "\nChoose mailbox [n]: ")
		mailboxIdx, ok := readInt()
		if !ok {
			break
		} else if mailboxIdx+1 > len(mailboxes) {
			fmt.Fprintln(w, "Invalid mailbox number")
			continue
		}

		for {
			// Fetch messages
			msgs, err := mailbox.LoadMessageDir(filepath.Join(app.Mailbox().MBoxPath, mailboxes[mailboxIdx]))
			if err != nil {
				log.Fatal(err)
			} else if len(msgs) == 0 {
				fmt.Fprintf(w, "(empty)\n")
				break
			}

			// Print messages (sorted by date)
			sort.Sort(fbb.ByDate(msgs))
			printMessages(w, msgs)

			// Query user for message to print
			fmt.Fprintf(w, "Choose message [n]: ")
			msgIdx, ok := readInt()
			if !ok {
				break
			} else if msgIdx+1 > len(msgs) {
				fmt.Fprintf(w, "invalid message number\n")
				continue
			}
			printMsg(w, msgs[msgIdx])

			// Mark as read?
			if mailbox.IsUnread(msgs[msgIdx]) {
				fmt.Fprintf(w, "Mark as read? [Y/n]: ")
				ans := readLine()
				if ans == "" || strings.EqualFold(ans, "y") {
					mailbox.SetUnread(msgs[msgIdx], false)
				}
			}

		L:
			for {
				fmt.Fprintf(w, "Action [C,r,ra,f,e,d,q,?]: ")
				switch ans := readLine(); ans {
				case "C", "c", "":
					break L
				case "d":
					fmt.Fprint(w, "Delete message? [y/N]: ")
					if ans := readLine(); strings.EqualFold(ans, "y") {
						msg := msgs[msgIdx]
						mbox := mailboxes[mailboxIdx]
						path := filepath.Join(app.Mailbox().MBoxPath, mbox, msg.MID()+mailbox.Ext)
						if err := os.Remove(path); err != nil {
							log.Printf("Failed to delete message %s from %s: %v", msg.MID(), mbox, err)
						} else {
							fmt.Fprintln(w, "Message deleted.")
						}
						break L
					}
				case "r":
					composeMessage(app, composerFlags{from: app.Options().MyCall, inReplyTo: msgs[msgIdx].MID()}, true)
				case "ra":
					composeMessage(app, composerFlags{from: app.Options().MyCall, inReplyTo: msgs[msgIdx].MID(), replyAll: true}, true)
				case "f":
					composeMessage(app, composerFlags{from: app.Options().MyCall, forward: msgs[msgIdx].MID()}, true)
				case "e":
					ExtractMessageHandle(ctx, app, []string{msgs[msgIdx].MID()})
				case "q":
					return
				case "?":
					fallthrough
				default:
					fmt.Fprintln(w, "c  - continue")
					fmt.Fprintln(w, "r  - reply")
					fmt.Fprintln(w, "ra - reply all")
					fmt.Fprintln(w, "f  - forward")
					fmt.Fprintln(w, "e  - extract (attachments)")
					fmt.Fprintln(w, "d  - delete")
					fmt.Fprintln(w, "q  - quit")
				}
			}
		}
	}
}

func readInt() (int, bool) {
	str := readLine()
	if str == "" {
		return 0, false
	}
	i, _ := strconv.Atoi(str)
	return i, true
}

func printMsg(w io.Writer, msg *fbb.Message) {
	fmt.Fprintf(w, "========================================\n")
	fmt.Fprintln(w, msg)
	fmt.Fprintf(w, "========================================\n\n")
}

func printMailboxes(w io.Writer) {
	for i, mbox := range mailboxes {
		fmt.Fprintf(w, "%d:%s\t", i, mbox)
	}
}

func printMessages(w io.Writer, msgs []*fbb.Message) {
	rows := make([][]string, len(msgs))
	for i, msg := range msgs {
		var to string
		if len(msg.To()) > 0 {
			to = msg.To()[0].Addr
		}
		if len(msg.To()) > 1 {
			to += ", ..."
		}

		var flags string
		if mailbox.IsUnread(msg) {
			flags += "N" // New
		}

		rows[i] = []string{
			fmt.Sprintf("%2d", i),
			flags,
			msg.Subject(),
			msg.From().Addr,
			msg.Date().String(),
			to,
		}
	}
	t := gotabulate.Create(rows)
	t.SetHeaders([]string{"i", "Flags", "Subject", "From", "Date", "To"})
	t.SetAlign("left")
	t.SetWrapStrings(true)
	t.SetMaxCellSize(60)
	fmt.Fprintln(w, t.Render("simple"))
}
