package commands

import (
	"context"
	"flag"
	"os"

	"github.com/google/subcommands"
	"github.com/inconshreveable/log15"
	c "github.com/mozqnet/go-exploitdb/config"
	"github.com/mozqnet/go-exploitdb/db"
	"github.com/mozqnet/go-exploitdb/fetcher"
	"github.com/mozqnet/go-exploitdb/models"
	"github.com/mozqnet/go-exploitdb/util"
	"github.com/spf13/viper"
)

// FetchExploitCmd :
type FetchExploitCmd struct {
	Debug    bool
	DebugSQL bool
	Quiet    bool
	LogDir   string
	LogJSON  bool
	Deep     bool

	DBPath string
	DBType string

	HTTPProxy string
}

// Name :
func (*FetchExploitCmd) Name() string { return "fetch" }

// Synopsis :
func (*FetchExploitCmd) Synopsis() string { return "Fetch Exploit" }

// Usage :
func (*FetchExploitCmd) Usage() string {
	return `fetch:
	fetch
		[-dbpath=$PWD/exploitdb.sqlite3 or connection string]
		[-dbtype=sqlite3|mysql|postgres|redis]
		[-http-proxy=http://192.168.0.1:8080]
		[-debug]
		[-debug-sql]
		[-quiet]
		[-deep]
		[-log-dir=/path/to/log]
		[-log-json]
  Command:
	$ go-exploitdb fetch

`
}

// SetFlags :
func (p *FetchExploitCmd) SetFlags(f *flag.FlagSet) {
	f.BoolVar(&p.Debug, "debug", false, "debug mode")
	f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode")
	f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)")

	defaultLogDir := util.GetDefaultLogDir()
	f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log")
	f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON")
	f.BoolVar(&p.Deep, "deep", false, "deep mode extract cve-id from github sources")

	pwd := os.Getenv("PWD")
	f.StringVar(&p.DBPath, "dbpath", pwd+"/go-exploitdb.sqlite3",
		"/path/to/sqlite3 or SQL connection string")

	f.StringVar(&p.DBType, "dbtype", "sqlite3",
		"Database type to store data in (sqlite3, mysql, postgres or redis supported)")

	f.StringVar(
		&p.HTTPProxy,
		"http-proxy",
		"",
		"http://proxy-url:port (default: empty)",
	)
}

// Execute :
func (p *FetchExploitCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	c.CommonConf.Quiet = p.Quiet
	c.CommonConf.DebugSQL = p.DebugSQL
	c.CommonConf.Debug = p.Debug
	c.CommonConf.DBPath = p.DBPath
	c.CommonConf.DBType = p.DBType
	c.CommonConf.HTTPProxy = p.HTTPProxy
	c.CommonConf.Deep = p.Deep

	util.SetLogger(p.LogDir, c.CommonConf.Quiet, c.CommonConf.Debug, p.LogJSON)
	if !c.CommonConf.Validate() {
		return subcommands.ExitUsageError
	}

	log15.Info("Initialize Database")
	driver, locked, err := db.NewDB(c.CommonConf.DBType, c.CommonConf.DBPath, c.CommonConf.DebugSQL)
	if err != nil {
		if locked {
			log15.Error("Failed to initialize DB. Close DB connection before fetching", "err", err)
		}
		return subcommands.ExitFailure
	}
	if err := driver.MigrateDB(); err != nil {
		log15.Error("Failed to migrate DB", "err", err)
		return subcommands.ExitFailure
	}

	log15.Info("Fetching Exploit")
	var exploits []models.Exploit
	if exploits, err = fetcher.FetchExploitDB(c.CommonConf.Deep); err != nil {
		log15.Error("Failed to fetch Exploit", "err", err)
		return subcommands.ExitFailure
	}

	log15.Info("Insert Exploit into go-exploitdb.", "db", driver.Name())
	if err := driver.InsertExploit(exploits); err != nil {
		log15.Error("Failed to insert.", "dbpath", viper.GetString("dbpath"), "err", err)
		return subcommands.ExitFailure
	}
	return subcommands.ExitSuccess
}
