package impl

import (
	"context"

	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic/pkg/log"
	"github.com/pingcap-inc/clinic/pkg/ngm/statements/aggregate"
	"github.com/pingcap-inc/clinic/pkg/ngm/statements/api"
	"github.com/pingcap-inc/clinic/pkg/ngm/statements/filter"
	"github.com/pingcap-inc/clinic/pkg/ngm/statements/group"
	"github.com/pingcap-inc/clinic/pkg/ngm/statements/load"
)

const maxDigestTextLengthInList = 1000

func (s *StatementImpl) List(ctx context.Context, param *api.ListParam) ([]*api.Model, error) {
	loadParam := &load.LoadParam{
		Env:           s.env,
		Provider:      param.Provider,
		Region:        param.Region,
		OrgID:         param.OrgID,
		ProjectID:     param.ProjectID,
		ClusterID:     param.ClusterID,
		SharedTier:    param.SharedTier,
		StorageBucket: param.StorageBucket,
		StoragePrefix: param.StoragePrefix,
		StartUnixSec:  param.StartUnixSec,
		EndUnixSec:    param.EndUnixSec,
	}
	rows, err := load.Load(ctx, loadParam, s.sf, s.cache)
	if err != nil {
		log.Warn(ctx, "load failed", zap.Error(err))
		return nil, ErrLoadFailed
	}

	flt := filter.Filters(
		filter.ByTimeRange(param.StartUnixSec, param.EndUnixSec),
		filter.BySchemas(param.Schemas),
		filter.ByStmtTypes(param.StmtTypes),
		filter.ByText(param.Text),
		filter.DiscardInternalSQL(),
		filter.DiscardCloudAdminUser(),
	)
	grp := group.BySchemaAndDigest()
	agg := aggregate.AggregatorFactories(getStmtAggFactories(
		append(
			param.Fields,
			"schema_name",
			"digest",
			"sum_latency",
			"summary_begin_time",
			"summary_end_time",
		)),
	)

	statements, err := Execute(ctx, rows, flt, grp, agg)
	if err != nil {
		log.Warn(ctx, "execute failed", zap.Error(err))
		return nil, ErrExecuteFailed
	}

	return s.postProcessList(ctx, statements), nil
}

func (s *StatementImpl) postProcessList(ctx context.Context, statements []*api.Model) []*api.Model {
	log.Info(ctx, "post process statements list", zap.Int("count", len(statements)))
	// Sort list by latency.
	sortBySumLatency(statements)
	// Cut SQL text in list.
	for n := range statements {
		if len(statements[n].DigestText) > maxDigestTextLengthInList {
			statements[n].DigestText = statements[n].DigestText[:maxDigestTextLengthInList-3] + "..."
		}
	}
	return statements
}
