package proto

import (
	"fmt"
	"go.uber.org/zap"
	"io"
	"reflect"
	"strings"
	"github.com/pingcap/log"
	"github.com/lensesio/tableprinter"

)

type ConfInfo struct {
	UniTag      string `header:"UniTag"`
	Val         string `header:"val"`
	CheckResult string `header:"CheckResult"`
}

type ConfPrintTemplate struct {
	Rule     *Rule
	InfoList []*ConfInfo
}

func NewConfPrintTemplate(rule *Rule) *ConfPrintTemplate {
	return &ConfPrintTemplate{
		Rule: rule,
	}
}

func (c *ConfPrintTemplate) CollectResult(hd *HandleData, retValue interface{}) error {
	// rule valpath
	// find data val, host ip component warnlevel ......
	// fmt print and into instance checkinfo
	if hd == nil {
		return fmt.Errorf("handle data is nil")
	}
	if !hd.IsValid {
		confInfo := &ConfInfo{
			UniTag:      hd.UqiTag,
			Val:         "",
			CheckResult: "nodata",
		}
		c.InfoList = append(c.InfoList, confInfo)
		return nil
	}
	checkPass, ok := retValue.(bool)
	if !ok {
		return fmt.Errorf("retValue can't change to bool")
	}
	checkResult := ""
	if checkPass {
		checkResult = "OK"
	} else {
		checkResult = c.Rule.WarnLevel
	}
	valstr := c.GetValStr(hd)

	confInfo := &ConfInfo{
		UniTag:      hd.UqiTag,
		Val:         valstr,
		CheckResult: checkResult,
	}
	c.InfoList = append(c.InfoList, confInfo)
	return nil
}

func (c *ConfPrintTemplate) GetValStr(hd *HandleData) string {
	componentVal := c.SplitComponentAndPath(c.Rule.Variation)
	valmap := []string{}
	for _, data := range hd.Data {
		conf, ok := data.(Config)
		if !ok {
			log.Error("can't convert into config type", zap.String("acting name",data.ActingName()))
			continue
		}
		valpaths := componentVal[conf.GetComponent()]
		for _, valpath := range valpaths {
			if len(valpath) != 0 {
				rv := conf.GetValueByTagPath(valpath)
				valmap = append(valmap, fmt.Sprintf("%s.%s:%v", conf.GetComponent(), valpath, rv))
			}
		}
	}
	return strings.Join(valmap, ",")
}

func (c *ConfPrintTemplate) SplitComponentAndPath(_ string) map[string][]string {
	valpaths := strings.Split(c.Rule.Variation, ",")
	componentVal := make(map[string][]string)
	for _, valpath := range valpaths {
		nn := strings.Split(valpath, ".")
		if len(nn) < 2 {
			continue
		}
		valsplit := nn[1:]
		val := strings.Join(valsplit, ".")
		if vals, ok := componentVal[nn[0]]; ok {
			vals = append(vals, val)
			componentVal[nn[0]] = vals
		} else {
			componentVal[nn[0]] = []string{val}
		}
	}
	return componentVal
}

func (c *ConfPrintTemplate) Print(out io.Writer) {
	printer := tableprinter.New(out)
	for _, rr := range c.InfoList {
		row, nums := tableprinter.StructParser.ParseRow(reflect.ValueOf(rr).Elem())
		printer.RenderRow(row, nums)
	}
}

type SQLPerformancePrintTemplate struct {
	Rule     *Rule
	InfoList *SQLPerformanceInfo
}

type SQLPerformanceInfo struct {
	NumDigest string `header:"NumDigest"`
	Info      string `header:"Info"`
}

func NewSQLPerformancePrintTemplate(rule *Rule) *SQLPerformancePrintTemplate {
	return &SQLPerformancePrintTemplate{
		Rule: rule, // add
		InfoList: &SQLPerformanceInfo{
			Info: "Please check the collect csv file for specific information",
		},
	}
}

func (c *SQLPerformancePrintTemplate) CollectResult(hd *HandleData, retValue interface{}) error {
	if hd == nil {
		return fmt.Errorf("handle data is nil")
	}
	if !hd.IsValid {
		c.InfoList.NumDigest = "no collector data"
		return nil
	}
	data, ok := hd.Data[0].(*DashboardData)
	if !ok {
		log.Error("convert into dashboard data failed", zap.String("acting name", data.ActingName()))
	}
	switch c.Rule.Name {
	case "poor_effective_plan":
		checkResult, ok := retValue.(int64)
		if !ok {
			return fmt.Errorf("retValue to int failed, %v", reflect.TypeOf(retValue))
		}
		c.InfoList.NumDigest = fmt.Sprintf("%d Digest trigger cordon", checkResult)
	case "old_version_count":
		checkResult, ok := retValue.(bool)
		if !ok {
			return fmt.Errorf("retValue to bool failed, %v", retValue)
		}
		if !checkResult {
			c.InfoList.NumDigest = fmt.Sprintf("%d Digest trigger cordon", 0)
		} else {
			c.InfoList.NumDigest = fmt.Sprintf("%d Digest trigger cordon", data.OldVersionProcesskey.Count)
		}
	case "scan_key_skip":
		c.InfoList.NumDigest = fmt.Sprintf("%d Digest trigger cordon", data.TombStoneStatistics.Count)
	}
	return nil
}

func (c *SQLPerformancePrintTemplate) Print(out io.Writer) {
	printer := tableprinter.New(out)
	row, nums := tableprinter.StructParser.ParseRow(reflect.ValueOf(c.InfoList).Elem())
	printer.RenderRow(row, nums)
}