package gonmap

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/lcvvvv/gonmap/simplenet"
)

type probe struct {
	//探针级别
	rarity int
	//探针名称
	name string
	//探针适用默认端口号
	ports PortList
	//探针适用SSL端口号
	sslports PortList

	//totalwaitms  time.Duration
	//tcpwrappedms time.Duration

	//探针对应指纹库
	matchGroup []*match
	//探针指纹库若匹配失败，则会尝试使用fallback指定探针的指纹库
	fallback string

	//探针发送协议类型
	protocol string
	//探针发送数据
	sendRaw string
}

func (p *probe) scan(host string, port int, tls bool, timeout time.Duration, size int) (string, bool, error) {
	uri := fmt.Sprintf("%s:%d", host, port)

	sendRaw := strings.Replace(p.sendRaw, "{Host}", fmt.Sprintf("%s:%d", host, port), -1)

	text, err := simplenet.Send(p.protocol, tls, uri, sendRaw, timeout, size)
	if err == nil {
		return text, tls, nil
	}
	if strings.Contains(err.Error(), "STEP1") && tls {
		text, err := simplenet.Send(p.protocol, false, uri, p.sendRaw, timeout, size)
		return text, false, err
	}
	return text, tls, err
}

func (p *probe) match(s string) *FingerPrint {
	var f = &FingerPrint{}
	var softFilter string

	for _, m := range p.matchGroup {
		//实现软筛选
		if softFilter != "" {
			if m.service != softFilter {
				continue
			}
		}
		//logger.Println("开始匹配正则：", m.service, m.patternRegexp.String())
		if m.patternRegexp.MatchString(s) {
			//标记当前正则
			f.MatchRegexString = m.patternRegexp.String()
			if m.soft {
				//如果为软捕获，这设置筛选器
				f.Service = m.service
				softFilter = m.service
				continue
			} else {
				//如果为硬捕获则直接获取指纹信息
				m.makeVersionInfo(s, f)
				f.Service = m.service
				return f
			}
		}
	}
	return f
}

var probeExprRegx = regexp.MustCompile(`^(UDP|TCP) ([a-zA-Z0-9-_./]+) (?:q\|([^|]*)\|)$`)
var probeIntRegx = regexp.MustCompile(`^(\d+)$`)
var probeStrRegx = regexp.MustCompile(`^([a-zA-Z0-9-_./]+)$`)

func parseProbe(lines []string) *probe {
	var p = &probe{}
	p.ports = emptyPortList
	p.sslports = emptyPortList
	for _, line := range lines {
		p.loadLine(line)
	}
	return p
}

func (p *probe) loadLine(s string) {
	//分解命令
	i := strings.Index(s, " ")
	commandName := s[:i]
	commandArgs := s[i+1:]
	//逐行处理
	switch commandName {
	case "Probe":
		p.loadProbe(commandArgs)
	case "match":
		p.loadMatch(commandArgs, false)
	case "softmatch":
		p.loadMatch(commandArgs, true)
	case "ports":
		p.loadPorts(commandArgs, false)
	case "sslports":
		p.loadPorts(commandArgs, true)
	case "totalwaitms":
		//p.totalwaitms = time.Duration(p.getInt(commandArgs)) * time.Millisecond
	case "tcpwrappedms":
		//p.tcpwrappedms = time.Duration(p.getInt(commandArgs)) * time.Millisecond
	case "rarity":
		p.rarity = p.getInt(commandArgs)
	case "fallback":
		p.fallback = p.getString(commandArgs)
	}
}

func (p *probe) loadProbe(s string) {
	//Probe <protocol> <probename> <probestring>
	if !probeExprRegx.MatchString(s) {
		panic(errors.New("probe 语句格式不正确"))
	}
	args := probeExprRegx.FindStringSubmatch(s)
	if args[1] == "" || args[2] == "" {
		panic(errors.New("probe 参数格式不正确"))
	}
	p.protocol = args[1]
	p.name = args[1] + "_" + args[2]
	str := args[3]
	str = strings.ReplaceAll(str, `\0`, `\x00`)
	str = strings.ReplaceAll(str, `"`, `${double-quoted}`)
	str = `"` + str + `"`
	str, _ = strconv.Unquote(str)
	str = strings.ReplaceAll(str, `${double-quoted}`, `"`)
	p.sendRaw = str
}

func (p *probe) loadMatch(s string, soft bool) {
	//"match": misc.MakeRegexpCompile("^([a-zA-Z0-9-_./]+) m\\|([^|]+)\\|([is]{0,2}) (.*)$"),
	//match <Service> <pattern>|<patternopt> [<versioninfo>]
	//	"matchVersioninfoProductname": misc.MakeRegexpCompile("p/([^/]+)/"),
	//	"matchVersioninfoVersion":     misc.MakeRegexpCompile("v/([^/]+)/"),
	//	"matchVersioninfoInfo":        misc.MakeRegexpCompile("i/([^/]+)/"),
	//	"matchVersioninfoHostname":    misc.MakeRegexpCompile("h/([^/]+)/"),
	//	"matchVersioninfoOS":          misc.MakeRegexpCompile("o/([^/]+)/"),
	//	"matchVersioninfoDevice":      misc.MakeRegexpCompile("d/([^/]+)/"),

	p.matchGroup = append(p.matchGroup, parseMatch(s, soft))
}

func (p *probe) loadPorts(expr string, ssl bool) {
	if ssl {
		p.sslports = parsePortList(expr)
	} else {
		p.ports = parsePortList(expr)
	}
}

func (p *probe) getInt(expr string) int {
	if !probeIntRegx.MatchString(expr) {
		panic(errors.New("totalwaitms or tcpwrappedms 语句参数不正确"))
	}
	i, _ := strconv.Atoi(probeIntRegx.FindStringSubmatch(expr)[1])
	return i
}

func (p *probe) getString(expr string) string {
	if !probeStrRegx.MatchString(expr) {
		panic(errors.New("fallback 语句参数不正确"))
	}
	return probeStrRegx.FindStringSubmatch(expr)[1]
}
