package async

import (
	"APIKiller/internal/core/data"
	"APIKiller/internal/core/database"
	"APIKiller/internal/core/notify"
	"APIKiller/pkg/logger"
	"github.com/tidwall/gjson"
	"strings"
	"time"

	"fmt"
	"io/ioutil"
	"net/http"
)

type AsyncCheckEngine struct {
	httpAPI      string
	lastRecordId string
}

func NewAsyncCheckEngine() *AsyncCheckEngine {
	return &AsyncCheckEngine{
		httpAPI:      "http://api.ceye.io/v1/records?token=0920449a5ed8b9db7a287a66a6632498&type=http",
		lastRecordId: "0",
	}
}

//
// Start
//  @Description: start to check
//  @receiver e
//
func (e *AsyncCheckEngine) Start() {
	// heart beat detection
	if !e.heartbeat() {
		logger.Errorln("cannot access target website successfully: http://api.ceye.io")
		return
	}

	// build a request
	request, _ := http.NewRequest("GET", e.httpAPI, nil)
	client := http.Client{}
	// polling API interface
	for {
		// make a http request
		response, _ := client.Do(request)

		// get data from json body
		if response.Body != nil {
			all, err := ioutil.ReadAll(response.Body)
			if err != nil {
				logger.Errorln(err)
			}
			results := gjson.Get(string(all), "data").Array()

			for _, result := range results {
				if result.Get("id").String() <= e.lastRecordId {
					continue
				}

				name := result.Get("name")
				token := strings.Replace(name.String(), "http://zpysri.ceye.io/", "", 1)

				go e.check(token)
			}

			if len(results) > 0 {
				e.lastRecordId = results[0].Get("id").String()
			}

		}

		// sleep
		time.Sleep(5 * 1000 * time.Millisecond)
	}
}

//
// heartbeat
//  @Description: check the health of http://api.ceye.io/ three times
//  @receiver e
//  @return bool
//
func (e *AsyncCheckEngine) heartbeat() bool {
	request, _ := http.NewRequest("GET", e.httpAPI, nil)
	client := http.Client{}

	for i := 0; i < 3; i++ {
		response, err := client.Do(request)
		if err != nil || response.StatusCode >= 500 {
			logger.Debugln(err)
		} else {
			return true
		}
	}
	return false
}

//
// check
//  @Description: notify async check token and update the corresponding vulnerability record
//  @receiver e
//  @param token
//
func (e *AsyncCheckEngine) check(token string) {
	logger.Infoln(fmt.Sprintf("[async check] token: %s", token))

	// notify
	notify.CreateNotification(&data.DataItem{
		Id:             "",
		Domain:         "异步检测",
		Url:            "",
		Method:         "",
		Https:          false,
		SourceRequest:  nil,
		SourceResponse: nil,
		VulnType:       token,
		VulnRequest:    nil,
		VulnResponse:   nil,
		ReportTime:     "",
		CheckState:     false,
	})

	// update database
	database.CreateUpdateTask(token)
}
