package asaModels

import (
	"regexp"
	"strings"

	"github.rackspace.com/gorax/tools/util"
)

var (
	ver3DESAESRE         = regexp.MustCompile(`(?i)(?:VPN-3DES-AES|Encryption-3DES-AES)\s+:\s+(\S+)`)
	verAddrRE            = regexp.MustCompile(`(?i)address\s+is\s+(.*?),`)
	verAnyConnectPremium = regexp.MustCompile(`(?i)(?:SSL\s+VPN\s+Peers|AnyConnect\s+Premium\s+Peers)\s+:\s+(\S+)`)
	verAsaRE             = regexp.MustCompile(`(?i)cisco\s+adaptive\s+security\s+appliance\s+software\s+version\s+([0-9]\S+)`)
	verAsdmRE            = regexp.MustCompile(`(?i)device\s+manager\s+version\s+([0-9].*?)\n`)
	verBootFileRE        = regexp.MustCompile(`(?i)config\s+file\s+at\s+boot\s+was\s+"(.*?)"`)
	verConfigRE          = regexp.MustCompile(`(?i)configuration\s+register\s+is\s+(\S+)\n`)
	verContextRE         = regexp.MustCompile(`(?i)cisco\s+adaptive\s+security\s+appliance\s+software\s+version\s+.*?(<context>)`)
	verDESRE             = regexp.MustCompile(`(?i)(?:VPN-DES|Encryption-DES)\s+:\s+(\S+)`)
	verHardwareExRE      = regexp.MustCompile(`(?i)hardware:\s+(\S+),\s+([0-9]+.*?)\s+ram,\s+cpu\s+(.*?),\s+(.*?)\s+asa:\s+([0-9]+.*?),\s+(.*?)\n`)
	verHardwareRE        = regexp.MustCompile(`(?i)hardware:\s+(\S+),\s+([0-9]+.*?)\s+ram,\s+cpu\s+(.*?)\n`)
	verImageRE           = regexp.MustCompile(`(?i)system\s+image\s+file\s+is\s+"(.*?)"`)
	verIntVerParseRE     = regexp.MustCompile(`(?i)([0-9]+):\s+(\S+):\s+(not used|\S+)\s+:\s+(.*?)\n`)
	verIrqRE             = regexp.MustCompile(`irq\s+([0-9]+)`)
	verLastModeRE        = regexp.MustCompile(`(?i)configuration\s+last\s+modified\s+by\s+(.*)`)
	verLicenseRE         = regexp.MustCompile(`(?i)this\s+platform\s+has\s+(?:an|a)\s+(.*?)(\s+\n|\n|$)`)
	verPermActKeyRE      = regexp.MustCompile(`(?i)running\s+permanent\s+activation\s+key:\s+(.*?)\n`)
	verPermRunKeyRE      = regexp.MustCompile(`(?i)running\s+activation\s+key:\s+(.*?)\n`)
	verRE                = regexp.MustCompile(`([0-9]+)\.([0-9]+)`)
	verSerialRE          = regexp.MustCompile(`(?i)serial\s+number:\s+(.*?)\n`)
	verSoftwareRE        = regexp.MustCompile(`([0-9]+)\.([0-9]+)(\(.*?)$`)
	verUpTimeRE          = regexp.MustCompile(`(?i)\S+\s+up\s+(.*?)\n`)
)

type (
	Version struct {
		AnyConnectPremium string           `json:"anyconnect_premium_peers"`
		BootConfigFile    string           `json:"boot_config_file"`
		ConfReg           string           `json:"config_register"`
		Context           bool             `json:"context"`
		DeviceManager     string           `json:"device_manager_version"`
		Encrypt3DESAES    bool             `json:"encryption-3des-aes"`
		EncryptDes        bool             `json:"encryption-3des"`
		EssentialsLicense bool             `json:"essentials-license"`
		Hardware          *Hardware        `json:"hardware"`
		Interfaces        []*VersionInt    `json:"interfaces"`
		LastModified      string           `json:"last_modified"`
		License           string           `json:"license"`
		PermanentActKey   string           `json:"permanent_activation_key"`
		Serial            string           `json:"serial"`
		SoftwareVer       *SoftwareVersion `json:"software_version"`
		SysImageFile      string           `json:"system_image_file"`
		Uptime            string           `json:"uptime"`
	}

	SoftwareVersion struct {
		Maintenance *string `json:"maintenance"`
		Major       int     `json:"major"`
		Minor       int     `json:"minor"`
		Version     *string `json:"version"`
	}

	VersionInt struct {
		Irq  *int    `json:"irq"`
		Mac  *string `json:"address"`
		Name *string `json:"name"`
		Slot *int    `json:"slot"`
		Type *string `json:"type"`
	}

	Hardware struct {
		ASA      *ASAHarware `json:"asa"`
		Cores    *string     `json:"cpu_cores"`
		CPU      *string     `json:"cpu"`
		Platform *string     `json:"platform"`
		RAM      *string     `json:"ram"`
	}

	ASAHarware struct {
		Cores *string `json:"cpu_cores"`
		RAM   *string `json:"ram"`
	}
)

func VersionParse(s string) *Version {
	v := new(Version)
	v.Parse(s)
	return v
}

func (v *Version) Parse(s string) {
	v.SoftwareVer = func(s string) *SoftwareVersion {
		if s == "" {
			return nil
		}
		ver := verSoftwareRE.FindStringSubmatch(s)
		if len(ver) != 0 {
			return &SoftwareVersion{
				Version:     &s,
				Major:       util.Atoi(ver[1]),
				Minor:       util.Atoi(ver[2]),
				Maintenance: &ver[3],
			}
		}
		ver = verRE.FindStringSubmatch(s)
		if len(ver) != 0 {
			return &SoftwareVersion{
				Version: &s,
				Major:   util.Atoi(ver[1]),
				Minor:   util.Atoi(ver[2]),
			}
		}
		return nil
	}(util.RegexpString(verAsaRE, s))

	// Determine if anyconnect essentials license is in use
	essentialsRes := regexp.MustCompile(`(?i)anyconnect essentials\s+: (\S+)`).FindStringSubmatch(s)
	if len(essentialsRes) > 0 {
		if strings.ToLower(essentialsRes[1]) != "disabled" {
			v.EssentialsLicense = true
		}
	}

	v.BootConfigFile = util.RegexpFindString(verBootFileRE, s)
	v.Context = verContextRE.MatchString(s)
	v.DeviceManager = util.RegexpFindString(verAsdmRE, s)
	v.SysImageFile = util.RegexpFindString(verImageRE, s)
	v.Hardware = func(s string) *Hardware {
		hw := verHardwareExRE.FindStringSubmatch(s)
		if len(hw) != 0 {
			return &Hardware{
				ASA: &ASAHarware{
					RAM:   &hw[5],
					Cores: &hw[6],
				},
				Cores:    &hw[4],
				CPU:      &hw[3],
				Platform: &hw[1],
				RAM:      &hw[2],
			}
		}
		hw = verHardwareRE.FindStringSubmatch(s)
		if len(hw) != 0 {
			return &Hardware{
				CPU:      &hw[3],
				Platform: &hw[1],
				RAM:      &hw[2],
			}
		}
		return nil
	}(s)

	v.AnyConnectPremium = util.RegexpFindString(verAnyConnectPremium, s)
	v.ConfReg = util.RegexpFindString(verConfigRE, s)
	v.Encrypt3DESAES = featureEnabled(util.RegexpFindString(ver3DESAESRE, s))
	v.EncryptDes = featureEnabled(util.RegexpFindString(verDESRE, s))
	v.intParse(s)
	v.LastModified = util.RegexpFindString(verLastModeRE, s)
	v.License = util.RegexpFindString(verLicenseRE, s)
	v.PermanentActKey = strings.TrimSpace(util.RegexpFindString(verPermActKeyRE, s))
	if v.PermanentActKey == "" {
		v.PermanentActKey = util.RegexpFindString(verPermRunKeyRE, s)
	}
	v.Serial = util.RegexpFindString(verSerialRE, s)
	v.Uptime = util.RegexpFindString(verUpTimeRE, s)
}

func featureEnabled(s string) bool {
	return strings.Contains(strings.ToLower(s), "enabled")
}

func (v *Version) intParse(s string) {
	ifNames := verIntVerParseRE.FindAllStringSubmatch(s, -1)
	if len(ifNames) != 0 {
		return
	}
	for _, t := range ifNames {
		switch t[3] {
		case "Not used":
			v.Interfaces = append(v.Interfaces, &VersionInt{
				Slot: util.AtoiPointer(t[1]),
				Type: &t[2],
				Name: &t[3],
				Mac:  nil,
				Irq:  util.RegexpAtoiPtr(verIrqRE, t[4]),
			})
		default:
			v.Interfaces = append(v.Interfaces, &VersionInt{
				Slot: util.AtoiPointer(t[1]),
				Type: &t[2],
				Name: &t[3],
				Mac:  util.RegexpFindStringPtr(verAddrRE, t[4]),
				Irq:  util.RegexpAtoiPtr(verIrqRE, t[4]),
			})
		}

	}
}

func (v *Version) LegacyASA() bool {
	if v.SoftwareVer.Major != -9999 && v.SoftwareVer.Minor != -9999 {
		if v.SoftwareVer.Major < 9 {
			if v.SoftwareVer.Minor < 3 {
				return true
			}
		}
	}
	return false
}
