package exploit

import (
	"encoding/json"
	"fmt"
	"net/http"

	cnf "github.com/future-architect/vuls/config"
	"github.com/future-architect/vuls/models"
	"github.com/future-architect/vuls/util"
	"github.com/mozqnet/go-exploitdb/db"
	exploitmodels "github.com/mozqnet/go-exploitdb/models"
	"github.com/parnurzeal/gorequest"
	"golang.org/x/xerrors"
)

// FillWithExploit fills exploit information that has in Exploit
func FillWithExploit(driver db.DB, r *models.ScanResult) (nExploitCve int, err error) {
	if cnf.Conf.Exploit.IsFetchViaHTTP() {
		var cveIDs []string
		for cveID := range r.ScannedCves {
			cveIDs = append(cveIDs, cveID)
		}
		prefix, _ := util.URLPathJoin(cnf.Conf.Exploit.URL, "cves")
		responses, err := getCvesViaHTTP(cveIDs, prefix)
		if err != nil {
			return 0, err
		}
		for _, res := range responses {
			exps := []*exploitmodels.Exploit{}
			if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
				return 0, err
			}
			exploits := ConvertToModels(exps)
			v, ok := r.ScannedCves[res.request.cveID]
			if ok {
				v.Exploits = exploits
			}
			r.ScannedCves[res.request.cveID] = v
			nExploitCve++
		}
	} else {
		if driver == nil {
			return 0, nil
		}
		for cveID, vuln := range r.ScannedCves {
			if cveID == "" {
				continue
			}
			es := driver.GetExploitByCveID(cveID)
			if len(es) == 0 {
				continue
			}
			exploits := ConvertToModels(es)
			vuln.Exploits = exploits
			r.ScannedCves[cveID] = vuln
			nExploitCve++
		}
	}
	return nExploitCve, nil
}

// ConvertToModels converts gost model to vuls model
func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
	for _, e := range es {
		var documentURL, shellURL *string
		if e.OffensiveSecurity != nil {
			os := e.OffensiveSecurity
			if os.Document != nil {
				documentURL = &os.Document.DocumentURL
			}
			if os.ShellCode != nil {
				shellURL = &os.ShellCode.ShellCodeURL
			}
		}
		exploit := models.Exploit{
			ExploitType:  e.ExploitType,
			ID:           e.ExploitUniqueID,
			URL:          e.URL,
			Description:  e.Description,
			DocumentURL:  documentURL,
			ShellCodeURL: shellURL,
		}
		exploits = append(exploits, exploit)
	}
	return exploits
}

// CheckHTTPHealth do health check
func CheckHTTPHealth() error {
	if !cnf.Conf.Exploit.IsFetchViaHTTP() {
		return nil
	}

	url := fmt.Sprintf("%s/health", cnf.Conf.Exploit.URL)
	var errs []error
	var resp *http.Response
	resp, _, errs = gorequest.New().Get(url).End()
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
		return xerrors.Errorf("Failed to connect to exploit server. url: %s, errs: %w", url, errs)
	}
	return nil
}

// CheckIfExploitFetched checks if oval entries are in DB by family, release.
func CheckIfExploitFetched(driver db.DB, osFamily string) (fetched bool, err error) {
	//TODO
	return true, nil
}

// CheckIfExploitFresh checks if oval entries are fresh enough
func CheckIfExploitFresh(driver db.DB, osFamily string) (ok bool, err error) {
	//TODO
	return true, nil
}
