package cmd

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

	"github.com/dominicbreuker/pspy/internal/config"
	"github.com/dominicbreuker/pspy/internal/fswatcher"
	"github.com/dominicbreuker/pspy/internal/logging"
	"github.com/dominicbreuker/pspy/internal/pspy"
	"github.com/dominicbreuker/pspy/internal/psscanner"
	"github.com/spf13/cobra"
)

var banner = `

     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     

`

var helpText = `
pspy monitors the system for file system events and new processes.
It prints these envents to the console.
File system events are monitored with inotify.
Processes are monitored by scanning /proc, using file system events as triggers.
pspy does not require root permissions do operate.
Check out https://github.com/dominicbreuker/pspy for more information.
`

var rootCmd = &cobra.Command{
	Use:   "pspy",
	Short: "pspy can watch your system for new processes and file system events",
	Long:  banner,
	Run:   root,
}

var logPS, logFS bool
var rDirs, dirs []string
var defaultRDirs = []string{
	"/usr",
	"/tmp",
	"/etc",
	"/home",
	"/var",
	"/opt",
}
var defaultDirs = []string{}
var triggerInterval int
var colored bool
var debug bool
var ppid bool
var cmdLength int

func init() {
	rootCmd.PersistentFlags().BoolVarP(&logPS, "procevents", "p", true, "print new processes to stdout")
	rootCmd.PersistentFlags().BoolVarP(&logFS, "fsevents", "f", false, "print file system events to stdout")
	rootCmd.PersistentFlags().StringArrayVarP(&rDirs, "recursive_dirs", "r", defaultRDirs, "watch these dirs recursively")
	rootCmd.PersistentFlags().StringArrayVarP(&dirs, "dirs", "d", defaultDirs, "watch these dirs")
	rootCmd.PersistentFlags().IntVarP(&triggerInterval, "interval", "i", 100, "scan every 'interval' milliseconds for new processes")
	rootCmd.PersistentFlags().BoolVarP(&colored, "color", "c", true, "color the printed events")
	rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "", false, "print detailed error messages")
	rootCmd.PersistentFlags().BoolVarP(&ppid, "ppid", "", false, "record process ppids")
	rootCmd.PersistentFlags().IntVarP(&cmdLength, "truncate", "t", 2048, "truncate process cmds longer than this")

	log.SetOutput(os.Stdout)
}

func root(cmd *cobra.Command, args []string) {
	logger := logging.NewLogger(debug)

	logger.Infof("%s", banner)

	cfg := &config.Config{
		RDirs:        rDirs,
		Dirs:         dirs,
		LogPS:        logPS,
		LogFS:        logFS,
		DrainFor:     1 * time.Second,
		TriggerEvery: time.Duration(triggerInterval) * time.Millisecond,
		Colored:      colored,
	}
	fsw := fswatcher.NewFSWatcher()
	defer fsw.Close()

	pss := psscanner.NewPSScanner(ppid, cmdLength)

	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	b := &pspy.Bindings{
		Logger: logger,
		FSW:    fsw,
		PSS:    pss,
	}
	exit := pspy.Start(cfg, b, sigCh)
	<-exit
	os.Exit(0)
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
