// Package quotedb implements a plugin to store quotes for channels. We pop them
// into a database to aid indexing / etc. Recommend SQLite.
package quotedb

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"sort"
	"strconv"
	"strings"

	"github.com/tcolgate/hugot"
)

type AddQuoteHandler struct {
	QuoteDB *QuoteDB
}

type LastQuoteHandler struct {
	QuoteDB *QuoteDB
}

type FindQuoteHandler struct {
	QuoteDB *QuoteDB
}

type RandQuoteHandler struct {
	QuoteDB *QuoteDB
}

type QuoteHandler struct {
	QuoteDB *QuoteDB
}

func (h *AddQuoteHandler) Describe() (string, string) {
	return "addquote", "Add a quote to the database"
}

func (h *AddQuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
	parts := strings.SplitN(m.Text, " ", 2)
	if len(parts) != 2 {
		return nil
	}

	quote, err := h.QuoteDB.AddQuote(m.Channel, parts[1], m.From)
	if err != nil {
		log.Printf("%s: AddQuote error: %v", m.Channel, err)
		return fmt.Errorf("Failed to add quote, sorry!")
	}

	w.Send(ctx, m.Reply(fmt.Sprintf("Quote %d added", quote.QuoteId)))
	return nil
}

func (h *LastQuoteHandler) Describe() (string, string) {
	return "lastquote", "Show the last-added quote"
}

func (h *LastQuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
	quote, err := h.QuoteDB.FindLastQuote(m.Channel)
	if err != nil {
		log.Printf("%s: Error in LastQuote: %v", m.Channel, err)
		return nil
	}

	w.Send(ctx, m.Reply(fmt.Sprintf("Quote %d by %s: %s", quote.QuoteId, quote.Author, quote.Data)))
	return nil
}

func (h *FindQuoteHandler) Describe() (string, string) {
	return "findquote", "Search for a quote by substring"
}

func (h *FindQuoteHandler) find(channel, term string) string {
	quotes, err := h.QuoteDB.FindQuotesByString(term, channel)
	if err != nil {
		return "Couldn't find any quotes matching search term"
	}

	switch len(quotes) {
	case 0:
		return "No results"
	case 1:
		return fmt.Sprintf("1 match. ID %d: %s: %s", quotes[0].QuoteId, quotes[0].Author, quotes[0].Data)
	}

	quote := quotes[0]
	quoteCount := strconv.Itoa(len(quotes))
	if len(quotes) > 5 {
		quoteCount = "More than 5"
	}

	quoteIds := make([]int, 0)
	max := 5
	if len(quotes) < max {
		max = len(quotes)
	}

	indices := rand.Perm(len(quotes))[0:max]
	for _, i := range indices {
		if quotes[i] == quote || quotes[i] == nil {
			continue
		}
		quoteIds = append(quoteIds, quotes[i].QuoteId)
	}
	quoteIds = append(quoteIds, quote.QuoteId)

	sort.Ints(quoteIds)
	quoteIdStrs := make([]string, len(quoteIds))
	for i, d := range quoteIds {
		quoteIdStrs[i] = strconv.Itoa(d)
	}
	idStr := strings.Join(quoteIdStrs, ", ")

	return fmt.Sprintf("%s matches. IDs: %s. Most recent: %s: %s", quoteCount, idStr, quote.Author, quote.Data)
}

func (h *FindQuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
	parts := strings.SplitN(m.Text, " ", 2)
	if len(parts) != 2 {
		return nil
	}

	w.Send(ctx, m.Reply(h.find(m.Channel, parts[1])))
	return nil
}

func (h *RandQuoteHandler) Describe() (string, string) {
	return "randquote", "Show a random quote"
}

func (h *RandQuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
	quote, err := h.QuoteDB.FindRandomQuote(m.Channel)
	if err != nil {
		log.Printf("%s: Error in RandQuote: %v", m.Channel, err)
		return nil
	}

	w.Send(ctx, m.Reply(fmt.Sprintf("Quote %d by %s: %s", quote.QuoteId, quote.Author, quote.Data)))
	return nil
}

func (h *QuoteHandler) Describe() (string, string) {
	return "quote", "show a specific quote, identified by ID"
}

func (h *QuoteHandler) show(channel, qid string) string {
	id, err := strconv.Atoi(qid)
	if err != nil {
		return "invalid ID"
	}

	quote, err := h.QuoteDB.FindQuoteByQuoteId(id, channel)
	if err != nil {
		return fmt.Sprintf("Couldn't find quote %d", id)
	}

	if quote.DeletedAt.Valid {
		delName := "(unknown)"
		if quote.DeletedBy.Valid {
			delName = quote.DeletedBy.String
		}

		return fmt.Sprintf("Quote %d was deleted by %s at %s", quote.Id, delName, quote.DeletedAt.Time.String())
	}

	return fmt.Sprintf("%s: %s", quote.Author, quote.Data)
}

func (h *QuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
	parts := strings.SplitN(m.Text, " ", 2)
	if len(parts) != 2 {
		return nil
	}

	w.Send(ctx, m.Reply(h.show(m.Channel, parts[1])))
	return nil
}
