package cronjob

import (
	"aon_app_server/models"
	"aon_app_server/utils/mysql"
	"bytes"
	"encoding/json"
	"github.com/beego/beego/v2/core/logs"
	beego "github.com/beego/beego/v2/server/web"
	"github.com/robfig/cron/v3"
	"io"
	"net/http"
	"sync"
)

var sendTask = cron.New(cron.WithSeconds())
var resultTask = cron.New(cron.WithSeconds())

var sendOffsetMutex sync.Mutex
var resultOffsetMutex sync.Mutex

func Start() {
	//defer loopCronTask.Stop()
	startSendTask()
	//startResultTask()
}

func startSendTask() {
	logs.Debug("startSendTask")
	//spec := "0 0 * * * ?" //"@every 1h"
	spec := "@every 1m" //"@every 1h"
	offset := int64(0)
	execTasks := make(chan *models.Task, 100)
	//spec := "*/1 * * * * ?" //"@every 1h"
	sendTask.AddFunc(spec, func() {
		logs.Debug("sendTask")
		tasks := queryPendingTask(offset)
		for _, value := range tasks {
			execTasks <- value
		}
		sendOffsetMutex.Lock()
		defer sendOffsetMutex.Unlock()
		offset += int64(len(tasks))
	})

	go func() {
		for task := range execTasks {
			go sendPendingTask(task)
		}
	}()

	sendTask.Start()
}

func queryPendingTask(offset int64) []*models.Task {
	qs := mysql.GetOrmer().QueryTable("task").
		Filter("status", 0).Offset(offset).Limit(5)
	var tasks []*models.Task
	qs.All(&tasks)
	return tasks
}

func sendPendingTask(task *models.Task) {
	host, _ := beego.AppConfig.String("taskUrl")
	url := host + task.ApiPath
	payload := new(bytes.Buffer)
	var input interface{}
	if err := json.Unmarshal([]byte(task.Input), &input); err != nil {
		logs.Info("sendPendingTask task.Input Unmarshal response:", err)
		return
	}
	json.NewEncoder(payload).Encode(input)
	client := &http.Client{}
	request, err := http.NewRequest("POST", url, payload)
	if err != nil {
		logs.Info("sendPendingTask Error NewRequest request:", err)
		return
	}
	if task.Auth != "" && len(task.Auth) > 32 {
		request.Header.Add("Authorization", "Bearer "+task.Auth)
	} else {
		request.Header.Add("apikey", task.Auth)
	}
	logs.Info("sendPendingTask client sending request:")
	resp, err := client.Do(request)
	if err != nil {
		logs.Info("sendPendingTask Error sending request:", err)
		return
	}
	defer resp.Body.Close()
	logs.Info("sendPendingTask resp code", resp.StatusCode)
	body, err := io.ReadAll(resp.Body)
	logs.Info("sendPendingTask body", string(body))
	if err != nil {
		logs.Info("sendPendingTask Error reading response:", err)
		return
	}
	var response models.TaskResponse
	if err = json.Unmarshal(body, &response); err != nil {
		logs.Info("sendPendingTask Error Unmarshal response:", err)
		return
	}
	if response.Task.IsSuccess {
		task.Status = 2
		data, err := json.Marshal(response.Output)
		if err != nil {
			logs.Info("sendPendingTask Output Unmarshal response:", err)
			return
		}
		task.Output = string(data)
		mysql.GetOrmer().Update(task, "status",
			"output")
	} else {
		task.Status = 3
		data, err := json.Marshal(response.Task)
		if err != nil {
			logs.Info("sendPendingTask response.Task Unmarshal response:", err)
			return
		}
		task.Error = string(data)
		mysql.GetOrmer().Update(task, "status",
			"error")
	}

}

func startResultTask() {
	logs.Debug("startResultTask")
	//spec := "0 0 * * * ?" //"@every 1h"
	spec := "@every 1h" //"@every 1h"
	offset := int64(0)
	//spec := "*/1 * * * * ?" //"@every 1h"
	resultTask.AddFunc(spec, func() {
		logs.Debug("resultTask")
		tasks := queryExecingTask(offset)
		for _, value := range tasks {
			go queryResult(value)
		}
		resultOffsetMutex.Lock()
		defer resultOffsetMutex.Unlock()
		offset += 5
	})
	resultTask.Start()
}

func queryExecingTask(offset int64) []*models.Task {
	qs := mysql.GetOrmer().QueryTable("task").
		Filter("status", 1).Offset(offset).Limit(5)
	var tasks []*models.Task
	qs.All(&tasks)
	return tasks
}

func queryResult(task *models.Task) {
	host, _ := beego.AppConfig.String("taskUrl")
	url := host + "/query/v1/" + task.TaskId
	payload := new(bytes.Buffer)
	json.NewEncoder(payload).Encode(task.Input)
	client := &http.Client{}
	request, err := http.NewRequest("POST", url, payload)
	if err != nil {
		logs.Info("queryResult Error NewRequest request:", err)
		return
	}
	if task.Auth != "" && len(task.Auth) > 32 {
		request.Header.Add("Authorization", "Bearer "+task.Auth)
	} else {
		request.Header.Add("apikey", task.Auth)
	}
	resp, err := client.Do(request)
	if err != nil {
		logs.Info("queryResult Error sending request:", err)
		return
	}
	defer resp.Body.Close()
	logs.Info("queryResult resp code", resp.StatusCode)
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		logs.Info("queryResult Error reading response:", err)
		return
	}
	var response models.TaskResponse
	if err = json.Unmarshal(body, &response); err != nil {
		logs.Info("queryResult Error Unmarshal response:", err)
		return
	}
	task.Status = 1
	//mysql.GetOrmer().Update(task, "status")
}
