package operateevent

import (
	"bytes"
	"database/sql"
	"fmt"
	"github.com/go-openapi/strfmt"
	"github.com/jinzhu/gorm"
	"github.com/pingcap/log"
	"github.com/pingcap/tep/db/entity"
	"github.com/pingcap/tep/pkg/apis/apiserver"
	"github.com/pingcap/tep/pkg/utils"
	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/runtime"
	"time"
)

type OperationLogQuery struct {
	StartTimeInSec int64
	EndTimeInSec   int64
	ManagementType string
	OperationType  string
	From           int
	Size           int
	OrderBy        string
	Desc           bool
}

const operationTimeDBColumnName = "operation_time"

var sortableFiled = map[string]string{
	"operationTime": operationTimeDBColumnName,
}

func (s *Storage) getAllOperationEvents(query *OperationLogQuery) ([]apiserver.OperateEvent, int64, error) {
	where, values := buildQuery(query)
	var operationLogs []entity.OperateEvent
	var total int64
	db := s.store.DB.Table("operate_event").Where(where, values...).Count(&total).
		Limit(query.Size).Offset(query.From).
		Order(utils.GetGORMSortByExpression(query.OrderBy, query.Desc)).
		Find(&operationLogs)
	apiItems := make([]apiserver.OperateEvent, len(operationLogs))
	for index, item := range operationLogs {
		apiItems[index] = apiserver.OperateEvent{
			ManagementType: item.ManagementType,
			OperationType:  item.OperationType,
			User:           item.User,
			OperationTime:  strfmt.DateTime(time.Unix(item.OperationTime, 0)).String(),
			Content:        item.Content,
		}
	}
	return apiItems, total, db.Error
}

func (s *Storage) downLoadOperateEvents(query *OperationLogQuery) (runtime.Object, error) {
	it := Iterator{db: s.store.DB, query: query}
	return utils.NewCSVStream(&it, 1024)
}

func buildQuery(query *OperationLogQuery) (string, []interface{}) {
	var values []interface{}
	where := bytes.Buffer{}
	where.WriteString(" 1=1  ")
	if query.ManagementType != "" {
		where.WriteString(" and management_type=? ")
		values = append(values, query.ManagementType)
	}
	if query.OperationType != "" {
		where.WriteString(" and operation_type=? ")
		values = append(values, query.OperationType)
	}
	//return 1 day log by default
	if query.StartTimeInSec <= 0 {
		query.StartTimeInSec = time.Now().Unix() - 24*60*60
	}
	if query.EndTimeInSec <= 0 {
		query.EndTimeInSec = time.Now().Unix()
	}
	where.WriteString(" and operation_time >= ? ")
	values = append(values, query.StartTimeInSec)
	where.WriteString(" and operation_time <= ? ")
	values = append(values, query.EndTimeInSec)
	return where.String(), values
}

type CSVOperateEvent struct {
}

type Iterator struct {
	rows  *sql.Rows
	db    *gorm.DB
	query *OperationLogQuery
}

func (it *Iterator) Open() error {
	where, values := buildQuery(it.query)
	rows, err := it.db.DB().Query("select management_type, operation_type, user,  operation_time, content from operate_event where "+where, values...)
	it.rows = rows
	return err
}

func (it *Iterator) Next() []string {
	if it.rows.Next() {
		event := apiserver.OperateEvent{}
		var optTime int64
		err := it.rows.Scan(&event.ManagementType, &event.OperationType, &event.User, &optTime, &event.Content)
		event.OperationTime = strfmt.DateTime(time.Unix(optTime, 0)).String()
		if err != nil {
			log.Info("get operate event from DB failed", zap.Error(err))
			return nil
		}
		return []string{event.ManagementType, event.OperationType, event.User, fmt.Sprintf("%v", event.OperationTime), event.Content}
	}
	return nil

}

func (it *Iterator) Close() error {
	return it.rows.Close()
}

func (it *Iterator) Header() []string {
	return []string{"management type", "operation type", "user", "operation time", "content"}
}
