package pkg

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
)

func (f5 *F5) PopulateLTMDataGroups() error {
	f5.log.Debug("PopulateLTMDataGroups() - Begin")
	defer f5.log.Debug("PopulateLTMDataGroups() - Complete")

	if err := f5.PopulateLTMExternalDataGroups(); err != nil {
		return err
	}
	if err := f5.PopulateLTMInternalDataGroups(); err != nil {
		return err
	}
	return nil
}

func (f5 *F5) PopulateLTMInternalDataGroups() error {
	f5.log.Debug("PopulateLTMInternalDataGroups() - Begin")
	defer f5.log.Debug("PopulateLTMInternalDataGroups() - Complete")

	f5.LTM.DataGroups.Internal = make(map[string]InternalDataGroup)

	resp, err := f5.sshClient.PostCommand("list ltm data-group internal", defaultPrompt, defaultTimeout)
	if err != nil {
		return err
	}

	names := regexp.MustCompile(`ltm data-group internal (\S+)`).FindAllStringSubmatch(*resp.Response, -1)
	for _, name := range names {
		f5.log.WithField("data-group", name[1]).Debug("gathering details")
		cmd := fmt.Sprintf("list ltm data-group internal %s all-properties one-line", name[1])
		res, err := f5.sshClient.PostCommand(cmd, defaultPrompt, defaultTimeout)
		if err != nil {
			return err
		}

		raw := *res.Response

		// Find the echo of the command we sent and remove it
		i := strings.Index(raw, "\n")
		if i != -1 {
			raw = raw[i+1 : len(raw)-1]
		}

		// Find the command line if it exists
		i = strings.Index(raw, "\n")
		if i != -1 {
			raw = raw[0:i]
		}

		dg, err := ParseInternalDataGroup(raw)
		if err != nil {
			return err
		}
		f5.LTM.DataGroups.Internal[name[1]] = *dg
	}

	return nil
}

func (f5 *F5) PopulateLTMExternalDataGroups() error {
	f5.log.Debug("PopulateLTMExternalDataGroups() - Begin")
	defer f5.log.Debug("PopulateLTMExternalDataGroups() - Complete")

	f5.LTM.DataGroups.External = make(map[string]ExternalDataGroup)

	res, err := f5.sshClient.PostCommand("list ltm data-group external", defaultPrompt, defaultTimeout)
	if err != nil {
		return err
	}

	dgs := regexp.MustCompile(`ltm data-group external (\S+)`).FindAllStringSubmatch(*res.Response, -1)
	for _, dg := range dgs {
		newGroup := ExternalDataGroup{
			Name: dg[1],
		}
		cmd := fmt.Sprintf("list ltm data-group external %s one-line all-properties", dg[1])
		resp, err := f5.sshClient.PostCommand(cmd, defaultPrompt, defaultTimeout)
		if err != nil {
			return err
		}
		if strings.Contains(*resp.Response, "WARNING - Received indecipherable byte(s)") {
			return errors.New("WARNING - Received indecipherable byte(s)")
		}

		ng, err := ParseExternalDataGroup(*resp.Response, f5)
		if err != nil {
			return err
		}

		f5.LTM.DataGroups.External[newGroup.Name] = *ng

	}

	return nil
}

func ParseInternalDataGroup(raw string) (*InternalDataGroup, error) {
	l := new(InternalDataGroup)
	l.Records = make(map[string]DataGroupRecord)

	f := strings.Fields(raw)
	for i := 0; i < len(f); i++ {
		switch f[i] {
		case "partition":
			l.Partition = f[i+1]
			i++
		case "description":
			l.Description = f[i+1]
			i++
		case "app-service":
			l.AppService = f[i+1]
			i++
		case "type":
			l.Type = f[i+1]
			i++
		}
	}

	// Gather the record entries
	recs := regexp.MustCompile(`(\S+) { data "(\S?)" }`).FindAllStringSubmatch(raw, -1)
	for _, rec := range recs {
		l.Records[rec[1]] = DataGroupRecord{
			Data: rec[2],
		}
	}

	return l, nil
}

func ParseExternalDataGroup(raw string, f5 *F5) (*ExternalDataGroup, error) {
	// Note : Can't create a _test for this function as it requires interactive access to the F5
	l := new(ExternalDataGroup)

	des := regexp.MustCompile(`description "(.*)" partition`).FindStringSubmatch(raw)
	if len(des) > 0 {
		l.Description = des[1]
	} else {
		l.Description = "none"
	}
	t := regexp.MustCompile(`type (\S+)`).FindStringSubmatch(raw)
	if len(t) > 0 {
		l.Type = t[1]
	}
	app := regexp.MustCompile(`app-service (\S+)`).FindStringSubmatch(raw)
	if len(app) > 0 {
		l.AppService = app[1]

	}
	part := regexp.MustCompile(`partition (\S+)`).FindStringSubmatch(raw)
	if len(part) > 0 {
		l.Partition = part[1]
	}

	ext := regexp.MustCompile(`external-file-name (\S+)`).FindStringSubmatch(raw)
	if len(ext) > 0 {
		l.ExternalFileName = ext[1]
	}

	// Search the bigip.conf file to find the actual filename
	cmd := fmt.Sprintf("run util bash -c \"grep %s /config/bigip.conf\"", l.ExternalFileName)
	resp, err := f5.sshClient.PostCommand(cmd, defaultPrompt, defaultTimeout)
	if err != nil {
		return nil, err
	}
	cp := regexp.MustCompile(`cache-path (.*)`).FindStringSubmatch(*resp.Response)
	if len(cp) > 0 {
		l.CachePath = cp[1]
		// cat the contents of the external file
		cmd := fmt.Sprintf("run util bash -c \"cat %s\"", l.CachePath)
		res, err := f5.sshClient.PostCommand(cmd, defaultPrompt, defaultTimeout)
		if err != nil {
			return nil, err
		}
		// split it in to individual strings
		split := strings.Split(*res.Response, "\n")
		for i, line := range split {
			if i == 0 {
				continue
			}
			// if this string contains the prompt at the end of the output, split it and only include the actual file contents
			if strings.Contains(line, "netsec.nsi@(") {
				s := strings.Split(line, "netsec.nsi@(")
				l.Contents = append(l.Contents, s[0])
			} else {
				l.Contents = append(l.Contents, line)
			}
		}
	}
	return l, nil
}
