package asaModels

import (
	"errors"
	"regexp"
	"strings"

	"github.rackspace.com/gorax/asaapi/pkg/logger"
	"github.rackspace.com/gorax/tools/util"
)

var (
	SHFOENABLERE                = regexp.MustCompile(`(?i)failover\s+(on)`)
	SHFOPRIRE                   = regexp.MustCompile(`(?i)this\s+host:((.|\n)*?)other\s+host:`)
	showFailoverContextPriRegex = regexp.MustCompile(`(?i)this\s+context:((.|\n)*?)peer\s+context:`)
	SHFOSECRE                   = regexp.MustCompile(`(?i)other\s+host:((.|\n)*?)stateful failover`)
	showFailoverContextSecRegex = regexp.MustCompile(`(?i)peer\s+context:((.|\n)*?)stateful failover`)
	//SHFOSTATERE                 = regexp.MustCompile(`(?i)stateful\s+failover\s+logical((.|\n)*?)$`)
	//SHFOUNITRE                  = regexp.MustCompile(`(?i)failover\s+unit\s+(\S+)`)
	//SHFOLANINTRE                = regexp.MustCompile(`(?i)failover\s+LAN\s+interface:\s+(.*?)\s+\(`)
	//SHFOLANSTATUSRE             = regexp.MustCompile(`(?i)failover\s+LAN\s+interface:\s+.*?\((.*?)\)`)
	//SHFOINTFREQRE               = regexp.MustCompile(`(?i)unit\s+poll\s+frequency\s+([0-9]+)`)
	//SHFOINTHOLDRE               = regexp.MustCompile(`(?i)holtime\s+([0-9]+)\s+`)
	//SHFOINTPOLRE                = regexp.MustCompile(`(?i)interface\s+policy\s+([0-9]+)`)
	//SHFOMONINTRE                = regexp.MustCompile(`(?i)monitored\s+interfaces\s+([0-9]+)`)
	//SHFOMAXMONINTRE             = regexp.MustCompile(`(?i)monitored\s+interaces\s+[0-9]+\s+of\s+([0-9]+)\s+max`)
	//SHFOVEROURRE                = regexp.MustCompile(`(?i)version:\s+ours\s+(\S+)s+`)
	//SHFOVERMATERE               = regexp.MustCompile(`(?i),\s+mate\s+(\S+)`)
	//SHFOLASTFORE                = regexp.MustCompile(`(?i)last\s+failover\s+at:\s+(.*?)$`)
	//SHFOLINKRE                  = regexp.MustCompile(`(?i)link\s+:\s+(\S+)\s+(\S+)\s+\((.*?)\)`)
	//SHFOGENERALRE               = regexp.MustCompile(`(?i)general\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOSYSCMDRE                = regexp.MustCompile(`(?i)sys\s+cmd\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOUPTIMERE                = regexp.MustCompile(`(?i)up\s+time\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFORPCSRVCRE               = regexp.MustCompile(`(?i)rpc\s+services\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOTCPCONNRE               = regexp.MustCompile(`(?i)tcp\s+conn\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOUDPCONNRE               = regexp.MustCompile(`(?i)udp\s+conn\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOARPTBLRE                = regexp.MustCompile(`(?i)arp\s+tbl\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOXLATETORE               = regexp.MustCompile(`(?i)xlate_timeout\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOIPV6TBL                 = regexp.MustCompile(`(?i)ipv6\s+nd\s+tbl\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOVPNIKEV1SARE            = regexp.MustCompile(`(?i)vpn\s+ikev1\s+sa\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOVPNIKEV1P2RE            = regexp.MustCompile(`(?i)vpn\s+ikev1\s+p2\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOVPNIKEv2SARE            = regexp.MustCompile(`(?i)vpn\s+ikev2\s+sa\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOVPNIKEV2P2RE            = regexp.MustCompile(`(?i)vpn\s+ikev2\s+p2\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOVPNCTCPUDPRE            = regexp.MustCompile(`(?i)vpn\s+ctcp\s+upd\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOVPNSDIUPDRE             = regexp.MustCompile(`(?i)vpn\s+sdi\s+upd\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOVPNDHCPUPDRE            = regexp.MustCompile(`(?i)vpn\s+ikev2\s+sa\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOSIPSESSIONRE            = regexp.MustCompile(`(?i)sip\s+session\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOROUTESESSRE             = regexp.MustCompile(`(?i)route\s+session\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOUSERIDNTYRE             = regexp.MustCompile(`(?i)user-identity\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOCTSSGTNAMERE            = regexp.MustCompile(`(?i)cts\s+sgtname\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOCTSPACRE                = regexp.MustCompile(`(?i)cts\s+pac\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOTRUSTSECSXPRE           = regexp.MustCompile(`(?i)trustsec-sxp\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOIPV6ROUTERE             = regexp.MustCompile(`(?i)ipv6\s+route\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFORECVQRE                 = regexp.MustCompile(`(?i)recv\s+q:\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	//SHFOXMITQRE                 = regexp.MustCompile(`(?i)xmit\s+q:\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)`)
	SHFOPRISECRE   = regexp.MustCompile(`(?i)(primary|secondary)\s+-\s+(.*?)\n`)
	SHFOACTVTIMERE = regexp.MustCompile(`(?i)active\s+time:\s+([0-9]+)\s+`)
	SHFOSLOTRE     = regexp.MustCompile(`(?i)(s|)lot\s+([0-9]+):\s+(.*?)\s+status\s+\((.*?)\)`)
	SHFOINTRE      = regexp.MustCompile(`(?i)interface\s+(\S+)\s+\((.*?)\):\s+(.*)`)
)

type (
	FailoverStatus struct {
		Enabled                bool          `json:"failover_enabled"`
		ThisHost               *FailoverUnit `json:"this_host"`
		OtherHost              *FailoverUnit `json:"other_host"`
		UpdateStats            *UpdateStats  `json:"update_stats"`
		FailoverUnit           *string       `json:"failover_unit"`
		FailoverLanInt         *string       `json:"failover_lan_interface"`
		FailoverLanIntStatus   *string       `json:"failover_lan_interface_status"`
		InterfacePollFrequency *int          `json:"interface_poll_frequency"`
		InterfacePollHoldTime  *int          `json:"interface_holdtime"`
		InterfacePolicy        *int          `json:"interface_policy"`
		MonitoredInterface     *int          `json:"monitored_interfaces"`
		MaxMonitorInterface    *int          `json:"max_monitored_interfaces"`
		VersionOurs            *string       `json:"our_version"`
		VersionMate            *string       `json:"mate_version"`
		LastFailover           *string       `json:"last_failover"`
	}

	FailoverUnit struct {
		Status       *string         `json:"status"`
		State        *string         `json:"active"`
		ActiveTime   *int            `json:"active_time"`
		FailoverSlot []*FailoverSlot `json:"slot"`
	}

	FailoverSlot struct {
		Name        *string        `json:"name"`
		SlotNumber  *int           `json:"slot_number"`
		Status      *string        `json:"status"`
		FailoverInt []*FailoverInt `json:"interfaces"`
	}

	FailoverInt struct {
		IfName    *string `json:"ifname"`
		IpAddress *string `json:"ip_address"`
		Status    *string `json:"status"`
	}

	UpdateStats struct {
		Link          *UpdateLink    `json:"link"`
		General       *StatefulObj   `json:"general"`
		SysCmd        *StatefulObj   `json:"sys_cmd"`
		UpTime        *StatefulObj   `json:"up_time"`
		RPCServices   *StatefulObj   `json:"rpc_services"`
		TCPConn       *StatefulObj   `json:"tcp_conn"`
		UDPConn       *StatefulObj   `json:"udp_conn"`
		ArpTable      *StatefulObj   `json:"arp_table"`
		XlateTimeout  *StatefulObj   `json:"xlate_timeout"`
		IPv6NdTbl     *StatefulObj   `json:"ipv6_nd_tbl"`
		VPNIkev1SA    *StatefulObj   `json:"vpn_ikev1_sa"`
		VPNIkev1P2    *StatefulObj   `json:"vpn_ikev1_p2"`
		VPNIkev2SA    *StatefulObj   `json:"vpn_ikev2_sa"`
		VPNIkev2P2    *StatefulObj   `json:"vpn_ikev2_p2"`
		VPNCTCPUpd    *StatefulObj   `json:"vpn_ctcp_upd"`
		VPNSDIUpd     *StatefulObj   `json:"vpn_sdi_upd"`
		VPNDHCPUpd    *StatefulObj   `json:"vpn_dhcp_upd"`
		SipSession    *StatefulObj   `json:"sip_session"`
		RouteSession  *StatefulObj   `json:"route_session"`
		UserIdentity  *StatefulObj   `json:"user_identity"`
		CtsSgtName    *StatefulObj   `json:"cts_sgtname"`
		CtsPac        *StatefulObj   `json:"cts_pac"`
		TrustSecSxp   *StatefulObj   `json:"trustsec_sxp"`
		IPv6Route     *StatefulObj   `json:"ipv6_route"`
		LogicalUpdate *LogicalUpdate `json:"logical_udpate_queue"`
	}

	LogicalUpdate struct {
		RecvQ *logicalQueue `json:"recv_q"`
		XmitQ *logicalQueue `json:"xmit_q"`
	}

	logicalQueue struct {
		Cur   *int `json:"current"`
		Max   *int `json:"max"`
		Total *int `json:"total"`
	}

	StatefulObj struct {
		Xmit *int `json:"xmit"`
		Xerr *int `json:"xerr"`
		Rcv  *int `json:"rcv"`
		Rerr *int `json:"rerr"`
	}

	UpdateLink struct {
		Name      *string `json:"name"`
		Interface *string `json:"interface"`
		Status    *string `jsoN:"status"`
	}
)

func (f *FailoverStatus) Parse(s string, c string) {
	f.Enabled = SHFOENABLERE.MatchString(s)
	if !f.Enabled {
		return
	}

	if c != "multiple" {
		pri := util.RegexpString(SHFOPRIRE, s)
		sec := util.RegexpString(SHFOSECRE, s)

		if pri != "" {
			f.ThisHost = UnitParse(pri)
		}
		if sec != "" {
			f.OtherHost = UnitParse(sec)
		}
		return
	}
	// Process multicontext
	var pri, sec string
	search := showFailoverContextPriRegex.FindStringSubmatch(s)
	if len(search) > 0 {
		pri = search[1]
	}
	search2 := showFailoverContextSecRegex.FindStringSubmatch(s)
	if len(search2) > 0 {
		sec = search2[1]
	}
	// pri := util.RegexpFindString(showFailoverContextPriRegex, s)
	// sec := util.RegexpFindString(showFailoverContextSecRegex, s)

	if pri != "" {
		f.ThisHost = UnitParse(pri)
	}
	if sec != "" {
		f.OtherHost = UnitParse(sec)
	}
}

func UnitParse(c string) *FailoverUnit {
	statusHA := SHFOPRISECRE.FindStringSubmatch(c)
	statusContext := regexp.MustCompile(`(?i)^ (.*?)\n`).FindStringSubmatch(c)
	if len(statusHA) == 0 && len(statusContext) == 0 {
		logger.Log.Println("SHOFOPRISECRE not found, exiting")
		return nil
	}
	var trimedStatus, s string
	if len(statusHA) > 0 {
		trimedStatus = strings.TrimSpace(statusHA[2])
		s = statusHA[1]
	} else {
		trimedStatus = strings.TrimSpace(statusContext[1])
	}

	// Remove white space which is sometimes left behind
	// trimedStatus := strings.TrimSpace(status[2])

	return &FailoverUnit{
		Status:       &s,
		State:        &trimedStatus,
		ActiveTime:   util.RegexpAtoiPtr(SHFOACTVTIMERE, c),
		FailoverSlot: SlotParse(c),
	}
}

func SlotParse(c string) (foslots []*FailoverSlot) {
	s := SHFOSLOTRE.FindAllStringSubmatch(c, -1)
	if len(s) == 0 {
		foslots = append(foslots, &FailoverSlot{
			FailoverInt: FailoverIntParse(c),
		})
		return
	}

	for _, slot := range s {
		foslots = append(foslots, &FailoverSlot{
			SlotNumber:  util.AtoiPointer(slot[2]),
			Name:        &slot[3],
			Status:      &slot[4],
			FailoverInt: FailoverIntParse(c),
		})
	}
	return
}

func FailoverIntParse(c string) (fi []*FailoverInt) {
	fip := SHFOINTRE.FindAllStringSubmatch(c, -1)
	if len(fip) == 0 {
		return
	}

	for _, i := range fip {
		stat := strings.TrimSpace(i[3])
		fi = append(fi, &FailoverInt{
			IfName:    &i[1],
			IpAddress: &i[2],
			Status:    &stat,
		})
	}

	return
}

func (f *FailoverStatus) IsActive() error {
	if f.Enabled == false {
		return nil
	}

	if f.ThisHost != nil {
		if strings.Contains(strings.ToLower(*f.ThisHost.State), "active") {
			return nil
		}
		return errors.New("Firewall status is not Active")
	}

	return errors.New("Unable to determine firewall status")
}

func (f *FailoverStatus) IsStandby() error {
	if f.Enabled == false {
		return nil
	}

	if f.ThisHost != nil {
		if strings.Contains(strings.ToLower(*f.ThisHost.State), "standby") {
			return nil
		}
		return errors.New("Firewall status is Active")
	}

	return errors.New("Unable to determine firewall status")
}
