package systemlog

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/elastic/go-elasticsearch/v6"
	"github.com/pingcap/log"
	"github.com/pingcap/tep/pkg/utils/es/query"
	"github.com/wushilin/stream"
	"go.uber.org/zap"
)

type systemQuery struct {
	from      int
	size      int
	startTime string
	endTime   string
	host      string
}

type systemLogQuery func(q *systemQuery)

var (
	AllHosts = "all"
)

func NewSystemQuery(q ...systemLogQuery) *systemQuery {
	tidbQ := &systemQuery{}
	stream.FromArray(q).Each(func(f interface{}) {
		f.(systemLogQuery)(tidbQ)
	})

	return tidbQ
}

func From(from int) systemLogQuery {
	if from < 0 {
		from = 0
	}

	return func(q *systemQuery) {
		q.from = from
	}
}

func Size(size int) systemLogQuery {
	if size <= 0 {
		size = 10
	}

	return func(q *systemQuery) {
		q.size = size
	}
}

func StartTimeInSec(startTimeInSec int64) systemLogQuery {
	if startTimeInSec <= 0 {
		startTimeInSec = time.Now().Unix() - 15*60
	}

	return func(q *systemQuery) {
		q.startTime = query.ConvertTimeStamp(startTimeInSec)
	}
}

func EndTimeInSec(endTimeInSec int64) systemLogQuery {
	if endTimeInSec <= 0 {
		endTimeInSec = time.Now().Unix()
	}

	return func(q *systemQuery) {
		q.endTime = query.ConvertTimeStamp(endTimeInSec)
	}
}

func Host(host string) systemLogQuery {
	if strings.ToLower(host) == AllHosts {
		host = ""
	}

	return func(q *systemQuery) {
		q.host = host
	}
}

func (q *systemQuery) Do(client *elasticsearch.Client, indexPrefix string) (*query.QueryResponse, error) {
	qb := query.NewQueryBuilder().
		AddEqual("host", q.host).
		AddRange("@timestamp", q.startTime, q.endTime).
		Sort("@timestamp", true).
		From(q.from).
		Size(q.size).
		BuildQuery()

	res, err := client.Search(
		client.Search.WithContext(context.Background()),
		client.Search.WithIndex(fmt.Sprintf("%s*", indexPrefix)),
		client.Search.WithBody(strings.NewReader(qb)),
		client.Search.WithPretty(),
	)

	if err != nil {
		log.Error("Query from elasticsearch failed", zap.Error(err))
		return nil, err
	}
	defer res.Body.Close()

	return query.NewResponse(res.Body), nil
}
