package debug

import (
	"context"
	"os"
	"text/template"

	"github.com/pingcap-inc/alioth/pkg/util"
	logpattern_go_proto "github.com/pingcap-inc/alioth/proto"
	"github.com/pingcap-inc/alioth/recorder"
	"github.com/pingcap-inc/alioth/storage"
	"github.com/pingcap/log"
	"github.com/spf13/cobra"
	"go.uber.org/zap"
)

var (
	StorePath  string
	ObjectName string
	SortMethod string
	Version    string
)

func NewDisplayCmd() *cobra.Command {
	cmdDisplay := &cobra.Command{
		Use:   "display",
		Short: "display pattern/coverage/unknown log data in given store",
		//Args:  cobra.MinimumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			Display(StorePath)
			return nil
		},
	}

	cmdDisplay.Flags().StringVar(&StorePath, "store", "", "the store path of log pattern/coverage/unknown logs")
	cmdDisplay.Flags().StringVar(&ObjectName, "object", "pattern", "display which object.(pattern, coverage, unknown)")
	cmdDisplay.Flags().StringVar(&SortMethod, "sort", "name", "sort method.(name or count)")
	cmdDisplay.Flags().StringVar(&Version, "version", "", "git hash/tag for log pattern/coverage(not for unknown)")
	cmdDisplay.MarkFlagRequired("store")
	return cmdDisplay
}

func Display(storePath string) error {
	templateContent := defaultPatternTemplate

	store, err := storage.NewStore(storePath)
	if err != nil {
		log.Fatal("access log pattern store failed", zap.String("path", storePath), zap.Error(err))
	}

	ls := NewLogSet()

	if ObjectName == "unknown" {
		templateContent = defaultUnknowsTemplate
		unknowLogs := recorder.NewUnknowLogRecorder(store)
		err = unknowLogs.LoadUnknowLogs(context.Background())
		if err != nil {
			log.Fatal("load unknow logs failed", zap.String("path", storePath), zap.Error(err))
		}

		unknowLogs.Forach(context.Background(), func(ctx context.Context, record *logpattern_go_proto.UnknowLogPattern) error {
			ld := UnKnowLogToDetail(record)
			ls.AddLog(record.GetPos().FilePath, ld)
			return nil
		})
	} else if ObjectName == "coverage" {
		templateContent = defaultCoverageTemplate
		cov := recorder.NewCoverager(store)
		err = cov.LoadCoverage(context.Background())
		if err != nil {
			log.Fatal("load coverage data failed", zap.String("path", storePath), zap.Error(err))
		}

		cov.Forach(context.Background(), func(ctx context.Context, record *logpattern_go_proto.Coverage) error {
			if util.CompareVersion(Version, record.Pos) {
				ld := CoverageToDetail(record)
				ls.AddLog(record.GetPos().FilePath, ld)
			}
			return nil
		})
	} else if ObjectName == "pattern" {
		coverages := make(map[string]*logpattern_go_proto.Coverage)

		store.ScanLogCoverage(context.Background(), func(key, value []byte) error {
			coverage := logpattern_go_proto.Coverage{}
			if err := coverage.Unmarshal(value); err != nil {
				log.Fatal("unmarshal coverage failed", zap.Error(err))
			}
			coverages[util.PosToStr(coverage.Pos)] = &coverage
			return nil
		})

		p := recorder.NewLogPattern(store)
		err = p.LoadPattern(context.Background())
		if err != nil {
			log.Fatal("load coverage data failed", zap.String("path", storePath), zap.Error(err))
		}

		p.Forach(context.Background(), func(ctx context.Context, record *logpattern_go_proto.LogPattern) error {
			if util.CompareVersion(Version, record.Pos) {
				posStr := util.PosToStr(record.Pos)
				coverage := coverages[posStr]
				ld := PatternToDetail(record, coverage)
				ls.AddLog(record.GetPos().FilePath, ld)
			}
			return nil
		})
	} else {
		log.Fatal("try to ./logcov debug display --object=pattern")
	}

	if SortMethod == "count" {
		ls.SortByCount()
	} else {
		ls.SortByKey()
	}

	t := template.New("store")
	render, err := t.Parse(templateContent)
	if err != nil {
		log.Fatal("parse template failed", zap.String("template", templateContent), zap.Error(err))
	}

	return render.Execute(os.Stdout, ls)
}
