package pkg

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

var (
	rgxLtmMonitorTypeName      = regexp.MustCompile(`(?i)ltm monitor\s+(\S+)\s+(\S+)\s+{`)
	rgxLtmMonitorCloseBracket  = regexp.MustCompile(`^\s*}\s*$`)
	rgxLtmHttpMonAppService    = regexp.MustCompile(`(?i)app-service\s+(\S+.*)`)
	rgxLtmHttpMonDescription   = regexp.MustCompile(`(?i)description\s+(\S+.*)`)
	rgxLtmHttpMonDestination   = regexp.MustCompile(`(?i)destination\s+(\S+.*)`)
	rgxLtmHttpMonInterval      = regexp.MustCompile(`(?i)interval\s+(\S+.*)`)
	rgxLtmHttpMonIpDscp        = regexp.MustCompile(`(?i)ip-dscp\s+(\S+.*)`)
	rgxLtmHttpMonManualResume  = regexp.MustCompile(`(?i)manual-resume\s+(\S+.*)`)
	rgxLtmHttpMonPassword      = regexp.MustCompile(`(?i)password\s+(\S+.*)`)
	rgxLtmHttpMonRecv          = regexp.MustCompile(`(?i)recv\s+(\S+.*)`)
	rgxLtmHttpMonRecvDisable   = regexp.MustCompile(`(?i)recv-disable\s+(\S+.*)`)
	rgxLtmHttpMonReverse       = regexp.MustCompile(`(?i)reverse\s+(\S+.*)`)
	rgxLtmHttpMonSend          = regexp.MustCompile(`(?i)send\s+(\S+.*)`)
	rgxLtmHttpMonTimeUntilUp   = regexp.MustCompile(`(?i)time-until-up\s+(\S+.*)`)
	rgxLtmHttpMonTimeout       = regexp.MustCompile(`(?i)timeout\s+(\S+.*)`)
	rgxLtmHttpMonTransparent   = regexp.MustCompile(`(?i)transparent\s+(\S+.*)`)
	rgxLtmHttpMonUpInterval    = regexp.MustCompile(`(?i)up-interval\s+(\S+.*)`)
	rgxLtmHttpMonUsername      = regexp.MustCompile(`(?i)username\s+(\S+.*)`)
	rgxLtmHttpMonCert          = regexp.MustCompile(`(?i)cert\s+(\S+.*)`)
	rgxLtmHttpMonCipherList    = regexp.MustCompile(`(?i)cipherlist\s+(\S+.*)`)
	rgxLtmHttpMonCompatibility = regexp.MustCompile(`(?i)compatibility\s+(\S+.*)`)
	rgxLtmHttpMonKey           = regexp.MustCompile(`(?i)key\s+(\S+.*)`)
)

type (
	LTMMonitors struct {
		HTTP          map[string]*HTTPMonitor  `json:"http"`
		HTTPS         map[string]*HTTPSMonitor `json:"https"`
		TCP           map[string]*TCPMonitor   `json:"tcp"`
		RawConfigText string                   `json:"raw_config_text"`
	}

	TCPMonitor struct {
		ProfileCommon
		Adaptive                 string   `json:"adaptive"`                   // disabled
		AdaptiveDivergencyType   string   `json:"adaptive-divergence-type"`   // relative
		AdaptiveDivergencyValue  int      `json:"adaptive-divergence-value"`  // 25
		AdaptiveLimit            int      `json:"adaptive-limit"`             // 200
		AdaptiveSamplingTimespan int      `json:"adaptive-sampling-timespan"` // 300
		Destination              string   `json:"destination"`                //destination *:*
		Interval                 int      `json:"interval"`                   //interval 5
		IPDSCP                   int      `json:"ip_dscp"`                    //ip-dscp 0
		ManualResume             string   `json:"manual_resume"`              //manual-resume disabled
		Recv                     string   `json:"recv"`                       //recv none
		RecvDisable              string   `json:"recv_disable"`               //recv-disable none
		Reverse                  string   `json:"reverse"`                    //reverse disabled
		Send                     []string `json:"send"`                       //send none
		TimeUntilUp              int      `json:"time_until_up"`              //time-until-up 0
		Timeout                  int      `json:"timeout"`                    //timeout 16
		Transport                string   `json:"transport"`                  //transparent disabled
		UpInterval               int      `json:"up_interval"`                //up-interval 0
	}

	HTTPMonitor struct {
		MonitorType  string `json:"monitor_type"`
		MonitorName  string `json:"monitor_name"`
		AppService   string `json:"app_service"`
		Description  string `json:"description"`
		Destination  string `json:"destination"`
		Interval     string `json:"interval"`
		IpDscp       string `json:"ip_dscp"`
		ManualResume bool   `json:"manual_resume"`
		Password     string `json:"password"`
		Recv         string `json:"recv"`
		RecvDisable  string `json:"recv_disable"`
		Reverse      bool   `json:"reverse"`
		Send         string `json:"send"`
		TimeUntilUp  string `json:"time_until_up"`
		Timeout      string `json:"timeout"`
		Transparent  bool   `json:"transparent"`
		UpInterval   string `json:"up_interval"`
		Username     string `json:"username"`
	}

	HTTPSMonitor struct {
		MonitorType   string `json:"monitor_type"`
		MonitorName   string `json:"monitor_name"`
		Cert          string `json:"cert"`
		CipherList    string `json:"cipher_list"`
		Compatibility bool   `json:"compatibility"`
		Destination   string `json:"destination"`
		Interval      string `json:"interval"`
		Key           string `json:"key"`
		ManualResume  bool   `json:"manual_resume"`
		Password      string `json:"password"`
		Recv          string `json:"recv"`
		RecvDisable   string `json:"recv_disable"`
		Reverse       bool   `json:"reverse"`
		Send          string `json:"send"`
		TimeUntilUp   string `json:"time_until_up"`
		Timeout       string `json:"timeout"`
		Transparent   bool   `json:"transparent"`
		UpInterval    string `json:"up_interval"`
	}
)

func (m *LTMMonitors) Parse(raw string, ignoreUnknownMonitors bool) error {
	m.RawConfigText = raw
	splitLines := strings.Split(raw, "\n")
	//clear out existing config to make sure we don't have stale data
	m.HTTP = make(map[string]*HTTPMonitor)
	//set up some booleans to skip regexing for data we already know we have
	inMonitor := false
	currentMonitorType := ""
	currentMonitorName := ""
	var monitorLines []string
	//loop through our config lines and pull out individual monitors for parsing
	for _, line := range splitLines {
		if !inMonitor {
			matchMonTypeName := rgxLtmMonitorTypeName.FindStringSubmatch(line)
			if len(matchMonTypeName) == 3 {
				//open monitor and grab lines
				inMonitor = true
				currentMonitorType = matchMonTypeName[1]
				currentMonitorName = matchMonTypeName[2]
				continue
			}
		} else {
			if rgxLtmMonitorCloseBracket.MatchString(line) {
				//close out our monitor and parse it
				switch currentMonitorType {
				case "http":
					monitor := parseHttpMonitor(currentMonitorType, currentMonitorName, monitorLines)
					m.HTTP[currentMonitorName] = monitor
				case "https":
					monitor := parseHttpsMonitor(currentMonitorType, currentMonitorName, monitorLines)
					m.HTTPS[currentMonitorName] = monitor
				default:
					if !ignoreUnknownMonitors {
						return fmt.Errorf("monitor type %s for monitor '%s' is not yet supported. Check RawConfigText for monitor config", currentMonitorType, currentMonitorName)
					}
				}
				//reset for next monitor
				inMonitor = false
				currentMonitorType = ""
				currentMonitorName = ""
				monitorLines = []string{}
				continue
			}
			monitorLines = append(monitorLines, line)
		}
	}
	return nil
}

func parseHttpMonitor(monitorType, monitorName string, lines []string) *HTTPMonitor {
	h := new(HTTPMonitor)
	h.MonitorType = monitorType
	h.MonitorName = monitorName
	for _, line := range lines {
		matchAppService := rgxLtmHttpMonAppService.FindStringSubmatch(line)
		if len(matchAppService) == 2 {
			h.AppService = trimQuotes(matchAppService[1])
			continue
		}
		matchDescription := rgxLtmHttpMonDescription.FindStringSubmatch(line)
		if len(matchDescription) == 2 {
			h.Description = trimQuotes(matchDescription[1])
			continue
		}
		matchDestination := rgxLtmHttpMonDestination.FindStringSubmatch(line)
		if len(matchDestination) == 2 {
			h.Destination = trimQuotes(matchDestination[1])
			continue
		}
		matchInterval := rgxLtmHttpMonInterval.FindStringSubmatch(line)
		if len(matchInterval) == 2 {
			h.Interval = trimQuotes(matchInterval[1])
			continue
		}
		matchIpDscp := rgxLtmHttpMonIpDscp.FindStringSubmatch(line)
		if len(matchIpDscp) == 2 {
			h.IpDscp = trimQuotes(matchIpDscp[1])
			continue
		}
		matchManualResume := rgxLtmHttpMonManualResume.FindStringSubmatch(line)
		if len(matchManualResume) == 2 {
			if strings.Contains(matchManualResume[1], "enable") {
				h.ManualResume = true
			}
			continue
		}
		matchPassword := rgxLtmHttpMonPassword.FindStringSubmatch(line)
		if len(matchPassword) == 2 {
			h.Password = trimQuotes(matchPassword[1])
			continue
		}
		matchRecv := rgxLtmHttpMonRecv.FindStringSubmatch(line)
		if len(matchRecv) == 2 {
			h.Recv = trimQuotes(matchRecv[1])
			continue
		}
		matchRecvDisable := rgxLtmHttpMonRecvDisable.FindStringSubmatch(line)
		if len(matchRecvDisable) == 2 {
			h.RecvDisable = trimQuotes(matchRecvDisable[1])
			continue
		}
		matchReverse := rgxLtmHttpMonReverse.FindStringSubmatch(line)
		if len(matchReverse) == 2 {
			if strings.Contains(matchReverse[1], "enable") {
				h.Reverse = true
			}
			continue
		}
		matchSend := rgxLtmHttpMonSend.FindStringSubmatch(line)
		if len(matchSend) == 2 {
			h.Send = trimQuotes(matchSend[1])
			continue
		}
		matchTimeUntilUp := rgxLtmHttpMonTimeUntilUp.FindStringSubmatch(line)
		if len(matchTimeUntilUp) == 2 {
			h.TimeUntilUp = trimQuotes(matchTimeUntilUp[1])
			continue
		}
		matchTimeout := rgxLtmHttpMonTimeout.FindStringSubmatch(line)
		if len(matchTimeout) == 2 {
			h.Timeout = trimQuotes(matchTimeout[1])
			continue
		}
		matchTransparent := rgxLtmHttpMonTransparent.FindStringSubmatch(line)
		if len(matchTransparent) == 2 {
			if strings.Contains(matchTransparent[1], "enable") {
				h.Transparent = true
			}
			continue
		}
		matchUpInterval := rgxLtmHttpMonUpInterval.FindStringSubmatch(line)
		if len(matchUpInterval) == 2 {
			h.UpInterval = trimQuotes(matchUpInterval[1])
			continue
		}
		matchUsername := rgxLtmHttpMonUsername.FindStringSubmatch(line)
		if len(matchUsername) == 2 {
			h.Username = trimQuotes(matchUsername[1])
			continue
		}
	}
	return h
}

func parseHttpsMonitor(monitorType, monitorName string, lines []string) *HTTPSMonitor {
	h := new(HTTPSMonitor)
	h.MonitorType = monitorType
	h.MonitorName = monitorName
	for _, line := range lines {
		matchCert := rgxLtmHttpMonCert.FindStringSubmatch(line)
		if len(matchCert) == 2 {
			h.Cert = trimQuotes(matchCert[1])
			continue
		}
		matchCipherList := rgxLtmHttpMonCipherList.FindStringSubmatch(line)
		if len(matchCipherList) == 2 {
			h.CipherList = trimQuotes(matchCipherList[1])
			continue
		}
		matchCompatibility := rgxLtmHttpMonCompatibility.FindStringSubmatch(line)
		if len(matchCompatibility) == 2 {
			if strings.Contains(matchCompatibility[1], "enable") {
				h.Compatibility = true
			}
			continue
		}
		matchDestination := rgxLtmHttpMonDestination.FindStringSubmatch(line)
		if len(matchDestination) == 2 {
			h.Destination = trimQuotes(matchDestination[1])
			continue
		}
		matchInterval := rgxLtmHttpMonInterval.FindStringSubmatch(line)
		if len(matchInterval) == 2 {
			h.Interval = trimQuotes(matchInterval[1])
			continue
		}
		matchKey := rgxLtmHttpMonKey.FindStringSubmatch(line)
		if len(matchKey) == 2 {
			h.Key = trimQuotes(matchKey[1])
			continue
		}
		matchManualResume := rgxLtmHttpMonManualResume.FindStringSubmatch(line)
		if len(matchManualResume) == 2 {
			if strings.Contains(matchManualResume[1], "enable") {
				h.ManualResume = true
			}
			continue
		}
		matchPassword := rgxLtmHttpMonPassword.FindStringSubmatch(line)
		if len(matchPassword) == 2 {
			h.Password = trimQuotes(matchPassword[1])
			continue
		}
		matchRecv := rgxLtmHttpMonRecv.FindStringSubmatch(line)
		if len(matchRecv) == 2 {
			h.Recv = trimQuotes(matchRecv[1])
			continue
		}
		matchRecvDisable := rgxLtmHttpMonRecvDisable.FindStringSubmatch(line)
		if len(matchRecvDisable) == 2 {
			h.RecvDisable = trimQuotes(matchRecvDisable[1])
			continue
		}
		matchReverse := rgxLtmHttpMonReverse.FindStringSubmatch(line)
		if len(matchReverse) == 2 {
			if strings.Contains(matchReverse[1], "enable") {
				h.Reverse = true
			}
			continue
		}
		matchSend := rgxLtmHttpMonSend.FindStringSubmatch(line)
		if len(matchSend) == 2 {
			h.Send = trimQuotes(matchSend[1])
			continue
		}
		matchTimeUntilUp := rgxLtmHttpMonTimeUntilUp.FindStringSubmatch(line)
		if len(matchTimeUntilUp) == 2 {
			h.TimeUntilUp = trimQuotes(matchTimeUntilUp[1])
			continue
		}
		matchTimeout := rgxLtmHttpMonTimeout.FindStringSubmatch(line)
		if len(matchTimeout) == 2 {
			h.Timeout = trimQuotes(matchTimeout[1])
			continue
		}
		matchTransparent := rgxLtmHttpMonTransparent.FindStringSubmatch(line)
		if len(matchTransparent) == 2 {
			if strings.Contains(matchTransparent[1], "enable") {
				h.Transparent = true
			}
			continue
		}
		matchUpInterval := rgxLtmHttpMonUpInterval.FindStringSubmatch(line)
		if len(matchUpInterval) == 2 {
			h.UpInterval = trimQuotes(matchUpInterval[1])
			continue
		}

	}
	return h
}

func trimQuotes(s string) string {
	s = strings.TrimSpace(s)
	s = strings.TrimLeft(s, "\"")
	s = strings.TrimRight(s, "\"")
	return s
}
