package impossible

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/rs/zerolog/log"

	"github.com/velour/catbase/bot"
	"github.com/velour/catbase/bot/msg"
	"github.com/velour/catbase/config"
)

type Impossible struct {
	b        bot.Bot
	c        *config.Config
	handlers bot.HandlerTable

	title   string
	content []string
	updated time.Time
	testing bool
}

func New(b bot.Bot) *Impossible {
	i := &Impossible{
		b:       b,
		c:       b.Config(),
		title:   "",
		content: []string{},
		updated: getTodaysMidnight().Add(time.Hour * -24),
		testing: false,
	}

	b.Register(i, bot.Help, i.help)
	i.register()

	return i
}

func newTesting(b bot.Bot) *Impossible {
	i := &Impossible{
		b:       b,
		c:       b.Config(),
		title:   "",
		content: []string{},
		updated: getTodaysMidnight().Add(time.Hour * -24),
		testing: true,
	}

	b.Register(i, bot.Help, i.help)
	i.register()

	return i
}

func (p *Impossible) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
	p.b.Send(c, bot.Message, message.Channel, "You don't need to do anything. I'll take care of it. But guess what I'm thinking.")
	return true
}

func (p *Impossible) tryRefresh(r bot.Request) (sent bool) {
	if p.updated.Before(time.Now()) {
		if p.title != "" {
			p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("The last impossible wikipedia article was: \"%s\"", p.title))
			sent = true
		}
		for !p.refreshImpossible() {
		}

		if p.testing {
			p.b.Send(r.Conn, bot.Message, r.Msg.Channel, p.title)
			sent = true
		}
	}
	return sent
}

func (p *Impossible) register() {
	p.handlers = bot.HandlerTable{
		{Kind: bot.Message, IsCmd: false,
			Regex: regexp.MustCompile(`(?i)^hint|clue$`),
			Handler: func(r bot.Request) bool {
				if p.tryRefresh(r) {
					return true
				}
				p.b.Send(r.Conn, bot.Message, r.Msg.Channel, p.content[rand.Intn(len(p.content))])
				return true
			}},
		{Kind: bot.Message, IsCmd: false,
			Regex: regexp.MustCompile(`(?i)^i friggin give up.?$`),
			Handler: func(r bot.Request) bool {
				if p.tryRefresh(r) {
					return true
				}
				p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("You guessed the last impossible wikipedia article: \"%s\"", p.title))
				for !p.refreshImpossible() {
				}
				return true
			}},
		{Kind: bot.Message, IsCmd: false,
			Regex: regexp.MustCompile(`.*`),
			Handler: func(r bot.Request) bool {
				if p.tryRefresh(r) {
					return true
				}

				if strings.Contains(strings.ToLower(r.Msg.Body), strings.ToLower(p.title)) {
					p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("You guessed the last impossible wikipedia article: \"%s\"", p.title))
					for !p.refreshImpossible() {
					}
					return true
				}
				return false
			}},
	}
	p.b.RegisterTable(p, p.handlers)
}

func (p *Impossible) refreshImpossible() bool {
	p.updated = getTodaysMidnight()
	resp, err := http.Get("https://en.wikipedia.org/wiki/Special:Random")
	if err != nil {
		log.Fatal().Err(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	titleRegex := regexp.MustCompile(`id="firstHeading"[^>]*(?P<Title>[^<]*)`)
	results := titleRegex.FindStringSubmatch(string(body))
	title := results[1][1:] //remove the leading <

	if title == "" {
		return false
	}

	p.title = title
	p.content = []string{}

	resp, err = http.Get("https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext&titles=" + url.PathEscape(title))
	if err != nil {
		log.Fatal().Err(err)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)

	var object map[string]interface{}
	json.Unmarshal([]byte(body), &object)

	pages := object["query"].(map[string]interface{})["pages"].(map[string]interface{})
	for _, page := range pages {
		descriptionText := page.(map[string]interface{})["extract"].(string)
		sentences := strings.Split(strings.ReplaceAll(descriptionText, "\n", " "), ". ")
		for _, sentence := range sentences {
			trimmed := strings.ToLower(strings.TrimSpace(sentence))
			if len(trimmed) == 0 || strings.HasPrefix(trimmed, "==") || len(strings.Split(trimmed, " ")) < 5 {
				continue
			}

			censored := strings.ReplaceAll(trimmed, strings.ToLower(title), "?????")

			p.content = append(p.content, censored)
		}
	}
	return true
}

func getTodaysMidnight() time.Time {
	now := time.Now()
	return time.Date(now.Year(), now.Month(), now.Day(), 24, 0, 0, 0, now.Location())
}