package sourcedata

import (
	"encoding/json"
	"os"
	"path"
	"strings"

	"github.com/pingcap-inc/checker/pkg/util/config"
	"github.com/sirupsen/logrus"

	"github.com/pingcap-inc/checker/proto"
	"gopkg.in/yaml.v3"
)

type Fetcher interface {
	FetchData() proto.SourceData
}

// TODO: move to consts pkg later
const (
	metaConfigFileName = "meta.yaml"
	clusterFileName    = "cluster-name.txt"
)

// FileFetcher load all needed data from file
type FileFetcher struct {
	dataDirPath string // dataDirPath point to a folder
	//rulePath string // rulePath point to a yaml file
}

func NewFileFetcher(dataDirPath string) (*FileFetcher, error) {
	ff := FileFetcher{
		dataDirPath: dataDirPath,
		//rulePath: rulePath,
	}
	return &ff, nil
}

// FetchData retrieve config data from file path, and filter rules by component version
// dataPath is the path to the top folder which contain the data collected by diag collect.
func (f *FileFetcher) FetchData(rules *config.RuleSpec) (*proto.SourceDataV2, proto.RuleSet, error) {
	// todo
	// decode meta.yaml
	meta := &proto.ClusterMetaData{}
	bs, err := os.ReadFile(f.genMetaConfigPath())
	if err != nil {
		logrus.Error(err)
		return nil, nil, err
	}
	clustername, err := os.ReadFile(f.genClusterNamePath())
	if err != nil {
		logrus.Error(err)
		return nil, nil, err
	}
	if err := yaml.Unmarshal(bs, meta); err != nil {
		logrus.Error(err)
		return nil, nil, err
	}
	rSet, err := rules.FilterOnVersion(meta.Version)
	if err != nil {
		logrus.Error(err)
		return nil, nil, err
	}
	splitpath := strings.Split(f.dataDirPath, "/")
	sampleid := ""
	if len(splitpath) > 0 {
		sampleid = splitpath[len(splitpath)-1]
	}
	sourceData := &proto.SourceDataV2{
		ClusterName: string(clustername),
		NodesData:   make(map[string][]proto.Config),
		TidbVersion: meta.Version,
		SampleData: proto.Sample{
			SampleId:      sampleid,
			SampleContent: f.GetComponent(meta),
		},
	}
	// decode config.json for related component
	for name, _ := range rSet {
		switch name {
		case proto.PdComponentName:
			for _, spec := range meta.Topology.PDServers {
				// todo add no data
				cfgPath := spec.RealtimeConfigPath(f.dataDirPath)
				bs, err := os.ReadFile(cfgPath)
				cfg := proto.NewPdConfigData()
				if err != nil {
					cfg.Config = nil // skip error
				} else {
					if err := json.Unmarshal(bs, cfg); err != nil {
						logrus.Error(err)
						return nil, nil, err
					}
				}
				cfg.Port = spec.ClientPort
				cfg.Host = spec.Host
				f.Append(sourceData, cfg, proto.PdComponentName)
			}
		case proto.TikvComponentName:
			for _, spec := range meta.Topology.TiKVServers {
				cfgPath := spec.RealtimeConfigPath(f.dataDirPath)
				bs, err := os.ReadFile(cfgPath)
				cfg := proto.NewTikvConfigData()
				if err != nil {
					cfg.TikvConfig = nil // skip error
				} else {
					if err := json.Unmarshal(bs, cfg); err != nil {
						logrus.Error(err)
						return nil, nil, err
					}
					cfg.LoadClusterMeta(meta)
				}
				cfg.Host = spec.Host
				cfg.Port = spec.Port
				f.Append(sourceData, cfg, proto.TikvComponentName)
			}
		case proto.TidbComponentName:
			for _, spec := range meta.Topology.TiDBServers {
				cfgPath := spec.RealtimeConfigPath(f.dataDirPath)
				bs, err := os.ReadFile(cfgPath)
				cfg := proto.NewTidbConfigData()
				if err != nil {
					cfg.Config = nil
				} else {
					if err := json.Unmarshal(bs, cfg); err != nil {
						logrus.Error(err)
						return nil, nil, err
					}
				}
				cfg.Host = spec.Host
				cfg.Port = spec.Port
				f.Append(sourceData, cfg, proto.TidbComponentName)
			}
		default:
		}
	}
	return sourceData, rSet, nil
}

func (f *FileFetcher) Append(sourceData *proto.SourceDataV2, cfg proto.Config, component string) {
	if n, ok := sourceData.NodesData[component]; ok {
		n = append(n, cfg)
		sourceData.NodesData[component] = n
	} else {
		n = []proto.Config{cfg}
		sourceData.NodesData[component] = n
	}
}

func (f *FileFetcher) genMetaConfigPath() string {
	return path.Join(f.dataDirPath, metaConfigFileName)
}

func (f *FileFetcher) genClusterNamePath() string {
	return path.Join(f.dataDirPath, clusterFileName)
}

func (f *FileFetcher) GetComponent(meta *proto.ClusterMetaData) []string {
	components := make([]string, 0)
	if len(meta.Topology.PDServers) != 0 {
		components = append(components, proto.PdComponentName)
	}
	if len(meta.Topology.TiDBServers) != 0 {
		components = append(components, proto.TidbComponentName)
	}
	if len(meta.Topology.TiKVServers) != 0 {
		components = append(components, proto.TikvComponentName)
	}
	if len(meta.Topology.TiFlashServers) != 0 {
		components = append(components, proto.TiflashComponentName)
	}
	return components
}
