package session

import (
	"fmt"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/bettercap/bettercap/v2/caplets"

	"github.com/bettercap/readline"

	"github.com/evilsocket/islazy/fs"
	"github.com/evilsocket/islazy/log"
)

func containsCapitals(s string) bool {
	for _, ch := range s {
		if ch < 133 && ch > 101 {
			return false
		}
	}
	return true
}

func (s *Session) setupReadline() (err error) {
	prefixCompleters := make([]readline.PrefixCompleterInterface, 0)
	for _, h := range s.CoreHandlers {
		if h.Completer == nil {
			prefixCompleters = append(prefixCompleters, readline.PcItem(h.Name))
		} else {
			prefixCompleters = append(prefixCompleters, h.Completer)
		}
	}

	tree := make(map[string][]string)
	for _, m := range s.Modules {
		for _, h := range m.Handlers() {
			if h.Completer == nil {
				parts := strings.Split(h.Name, " ")
				name := parts[0]

				if _, found := tree[name]; !found {
					tree[name] = []string{}
				}

				var appendedOption = strings.Join(parts[1:], " ")

				if len(appendedOption) > 0 && !containsCapitals(appendedOption) {
					tree[name] = append(tree[name], appendedOption)
				}
			} else {
				prefixCompleters = append(prefixCompleters, h.Completer)
			}
		}
	}

	for _, caplet := range caplets.List() {
		tree[caplet.Name] = []string{}
	}

	for root, subElems := range tree {
		item := readline.PcItem(root)
		item.Children = []readline.PrefixCompleterInterface{}
		for _, child := range subElems {
			item.Children = append(item.Children, readline.PcItem(child))
		}
		prefixCompleters = append(prefixCompleters, item)
	}

	history := ""
	if !s.Options.NoHistory {
		histPath := DefaultHistoryFile
		if fromEnv := os.Getenv(HistoryEnvVar); fromEnv != "" {
			histPath = fromEnv
		}
		history, _ = fs.Expand(histPath)
	}

	cfg := readline.Config{
		HistoryFile:     history,
		InterruptPrompt: "^C",
		EOFPrompt:       "^D",
		AutoComplete:    readline.NewPrefixCompleter(prefixCompleters...),
	}

	s.Input, err = readline.NewEx(&cfg)
	return err
}

func (s *Session) startNetMon() {
	// keep reading network events in order to add / update endpoints
	go func() {
		for event := range s.Queue.Activities {
			if !s.Active {
				return
			}

			if s.IsOn("net.recon") && event.Source {
				addr := event.IP.String()
				mac := event.MAC.String()

				existing := s.Lan.AddIfNew(addr, mac)
				if existing != nil {
					existing.LastSeen = time.Now()
				} else {
					existing, _ = s.Lan.Get(mac)
				}

				if existing != nil && event.Meta != nil {
					existing.OnMeta(event.Meta)
				}
			}
		}
	}()
}

func (s *Session) setupSignals() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		fmt.Println()
		s.Events.Log(log.WARNING, "Got SIGTERM")
		s.Close()
		os.Exit(0)
	}()
}

func (s *Session) setupEnv() {
	s.Env.Set("iface.index", fmt.Sprintf("%d", s.Interface.Index))
	s.Env.Set("iface.name", s.Interface.Name())
	s.Env.Set("iface.ipv4", s.Interface.IpAddress)
	s.Env.Set("iface.ipv6", s.Interface.Ip6Address)
	s.Env.Set("iface.mac", s.Interface.HwAddress)
	s.Env.Set("gateway.address", s.Gateway.IpAddress)
	s.Env.Set("gateway.mac", s.Gateway.HwAddress)

	if found, v := s.Env.Get(PromptVariable); !found || v == "" {
		if s.Interface.IsMonitor() {
			s.Env.Set(PromptVariable, DefaultPromptMonitor)
		} else {
			s.Env.Set(PromptVariable, DefaultPrompt)
		}
	}

	dbg := "false"
	if s.Options.Debug {
		dbg = "true"
	}
	s.Env.WithCallback("log.debug", dbg, func(newValue string) {
		newDbg := false
		if newValue == "true" {
			newDbg = true
		}
		s.Events.SetDebug(newDbg)
	})

	silent := "false"
	if s.Options.Silent {
		silent = "true"
	}
	s.Env.WithCallback("log.silent", silent, func(newValue string) {
		newSilent := false
		if newValue == "true" {
			newSilent = true
		}
		s.Events.SetSilent(newSilent)
	})
}
