package logic

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"sshd-tool/utils"
	"strconv"
	"strings"
)

var (
	// 需要的二进制文件
	NeedExec       = []string{"clamscan", "clamdscan"}
	ReInfectedFile = regexp.MustCompile(`(?i)^infected\s+files:\s+([0-9]+)\s*$`)
	ErrNoLineMatch = errors.New("no line match")

	HasGzip  = false
	HasBzip2 = false
	HasXZ    = false
	HasZStd  = false
	CMExec   = []string{"gzip", "bzip2", "xz", "zstd"}
)

func CheckExec() error {
	for _, v := range NeedExec {
		p := utils.FindCmd(v)
		if p == nil {
			return fmt.Errorf("%s not found", v)
		}
	}
	for k, v := range CMExec {
		if utils.FindCmd(v) != nil {
			switch k {
			case 0:
				HasGzip = true
			case 1:
				HasBzip2 = true
			case 2:
				HasXZ = true
			case 3:
				HasZStd = true
			}
		}
	}
	return nil
}

// todo 添加对tar、tgz等压缩文件的优化
// 压缩文件大小在500MB内，解压出流，让clamdscan扫描流

// ScanFile 扫描文件，返回是否有毒，如果有毒会删除
func ScanFile(f string) (bool, error) {
	finfo, err := os.Stat(f)
	if err != nil {
		return false, err
	}
	if finfo.IsDir() {
		return false, errors.New("error: dest is directory")
	}
	sf := fmt.Sprintf("%s.scanning", f)
	err = os.Rename(f, sf)
	if err != nil {
		return false, err
	}
	// // 优化归档文件
	// cm, istar, err := utils.DetectArchive(sf)
	// if err == nil {
	// 	if cm == utils.CMNone || cm == utils.CMUnknown {
	// 		// 不能采用优化方法
	// 	} else {
	// 		// 是压缩后的tar包
	// 		switch cm {
	// 		case utils.CMGZip:
	// 			if istar {
	// 				exec.Command()
	// 			} else {

	// 			}
	// 		case utils.CMBZip2:

	// 		case utils.CMXZ:

	// 		case utils.CMZStd:

	// 		}
	// 	}
	// }
	cmd := exec.Command("clamdscan", "-i", sf)
	output, err := cmd.CombinedOutput()
	if err != nil {
		if cmd.ProcessState.ExitCode() != 1 {
			return false, fmt.Errorf("run clamdscan failed, output: %s", string(output))
		}
	}
	if cmd.ProcessState.ExitCode() == 1 {
		// 返回值不为0，有异常
		str := string(output)
		num, err := parseClamOutput(str)
		if err != nil {
			return false, fmt.Errorf("parse clamdscan output error, output: %s", str)
		}
		if num > 0 {
			// todo：可以在这里插入日志
			os.Remove(sf)
		}
		return true, nil
	} else {
		// 无异常，直接返回
		err = os.Rename(sf, f)
		return false, err
	}
}

// parseClamOutput 解析clamdscan输出
func parseClamOutput(str string) (int, error) {
	for _, line := range strings.Split(strings.Trim(str, "\n"), "\n") {
		if ReInfectedFile.MatchString(line) {
			f := ReInfectedFile.FindStringSubmatch(line)
			return strconv.Atoi(f[1])
		}
	}
	return 0, ErrNoLineMatch
}
