package strat

import (
	"sort"
	"strconv"

	"github.com/curusarn/resh/pkg/records"
	"github.com/mb-14/gomarkov"
)

// MarkovChainCmd prediction/recommendation strategy
type MarkovChainCmd struct {
	Order       int
	history     []strMarkCmdHistoryEntry
	historyCmds []string
}

type strMarkCmdHistoryEntry struct {
	cmd     string
	cmdLine string
}

type strMarkCmdEntry struct {
	cmd       string
	transProb float64
}

// Init see name
func (s *MarkovChainCmd) Init() {
	s.history = nil
	s.historyCmds = nil
}

// GetTitleAndDescription see name
func (s *MarkovChainCmd) GetTitleAndDescription() (string, string) {
	return "command-based markov chain (order " + strconv.Itoa(s.Order) + ")", "Use command-based markov chain to recommend commands"
}

// GetCandidates see name
func (s *MarkovChainCmd) GetCandidates() []string {
	if len(s.history) < s.Order {
		var hist []string
		for _, item := range s.history {
			hist = append(hist, item.cmdLine)
		}
		return hist
	}
	chain := gomarkov.NewChain(s.Order)

	chain.Add(s.historyCmds)

	cmdsSet := map[string]bool{}
	var entries []strMarkCmdEntry
	for _, cmd := range s.historyCmds {
		if cmdsSet[cmd] {
			continue
		}
		cmdsSet[cmd] = true
		prob, _ := chain.TransitionProbability(cmd, s.historyCmds[len(s.historyCmds)-s.Order:])
		entries = append(entries, strMarkCmdEntry{cmd: cmd, transProb: prob})
	}
	sort.Slice(entries, func(i int, j int) bool { return entries[i].transProb > entries[j].transProb })
	var hist []string
	histSet := map[string]bool{}
	for i := len(s.history) - 1; i >= 0; i-- {
		if histSet[s.history[i].cmdLine] {
			continue
		}
		histSet[s.history[i].cmdLine] = true
		if s.history[i].cmd == entries[0].cmd {
			hist = append(hist, s.history[i].cmdLine)
		}
	}
	// log.Println("################")
	// log.Println(s.history[len(s.history)-s.order:])
	// log.Println(" -> ")
	// x := math.Min(float64(len(hist)), 3)
	// log.Println(entries[:int(x)])
	// x = math.Min(float64(len(hist)), 5)
	// log.Println(hist[:int(x)])
	// log.Println("################")
	return hist
}

// AddHistoryRecord see name
func (s *MarkovChainCmd) AddHistoryRecord(record *records.EnrichedRecord) error {
	s.history = append(s.history, strMarkCmdHistoryEntry{cmdLine: record.CmdLine, cmd: record.Command})
	s.historyCmds = append(s.historyCmds, record.Command)
	// s.historySet[record.CmdLine] = true
	return nil
}

// ResetHistory see name
func (s *MarkovChainCmd) ResetHistory() error {
	s.Init()
	return nil
}
