package common

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"git.listensoft.net/tool/jspkit/common/lxhttp"
	"git.listensoft.net/tool/jspkit/common/variable"
	"git.listensoft.net/tool/jspkit/logger"
	"git.listensoft.net/tool/jspkit/taxerr"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/tidwall/gjson"
	"io"
	"math/rand"
	"net/url"
	"os"
	"reflect"
	"regexp"
	"strings"
	"time"
)

type LvName string

const (
	CheckSuccess  LvName = "申报成功" // 检查成功 ------ 检查完全通过
	CheckFail     LvName = "检查失败" // 检查任务失败 ----- 网页打开失败等
	CheckOmit     LvName = "有遗漏"  // 检查有遗漏 ----- 未申报问题
	CheckAbnormal LvName = "有异常"  //检查有异常 ----- 税款问题
)

func GeneratePath(suffix string) string {
	ps := os.PathSeparator
	//检查路径
	if !PathExists("captcha" + string(ps)) {
		_ = os.MkdirAll("captcha"+string(ps), os.ModePerm)
	}
	return "captcha" + string(ps) + GetTimestamp(13) + "." + suffix
}

func PathExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

// 返回一个32位md5加密后的字符串
func Md5(data string) string {
	h := md5.New()
	h.Write([]byte(data))
	return hex.EncodeToString(h.Sum(nil))
}

/*
生成检查截图本地路径

		lv    图片对应的错误等级 用上面定义好的  Omit>Abnormal
		taxNo 税号
	    period 账期
		location 截图位置,例如申报清册、申报结果查询、税款缴纳、缴费缴纳查询
*/
func GenerateCheckImagePath(lv LvName, taxNo, period, location string) string {
	var path string
	path = "./data/check/" + taxNo + "/"
	if !PathExists(path) {
		os.MkdirAll(path, os.ModePerm)
	}
	return fmt.Sprintf(`%s-%s-%s-新版_%s_%s.png`, path+taxNo, period, GetTimestamp(13), location, lv)
}

func PostSbjt(taxno, period, path string) string {
	var params = map[string]string{
		"tax_no": taxno,
		"period": period,
		"type":   "etax", //电子税务局
	}
	str, err := PostFile("https://file.listensoft.net/api/v1/uploadsbjt", map[string]string{
		"file": path,
	}, params)
	if err != nil {
		log.Warnf("【%s】文件上传失败", path)
	}
	return gjson.GetBytes(str, "path").String()
}

// 获取申报截图的path
// taxNo 纳税人识别号 period 账期 taxType 任务名称
func GetSbjtPath(taxNo, period string, taxType variable.TaskName) string {
	var path string
	path = "./data/" + taxNo + "/"
	if !PathExists(path) {
		os.MkdirAll(path, os.ModePerm)
	}
	path = path + period + string(taxType) + "_" + GetTimestamp(13) + ".png"
	return path
}

// 判断字符串是否全是英文
func IsAllEnglish(s string) bool {
	// 检查字符串是否全部由ASCII字符组成
	for _, c := range s {
		if c > 127 {
			return false
		}
	}
	return true
}

// CreateFormReader 将 map 转换成 header
func CreateFormReader(data map[string]string) io.Reader {
	form := url.Values{}
	for k, v := range data {
		form.Add(k, v)
	}
	return strings.NewReader(form.Encode())
}

func Unbracket(str string) string {
	str = strings.ReplaceAll(str, "（", "(")
	str = strings.ReplaceAll(str, "）", ")")
	str = strings.ReplaceAll(str, " ", "")
	return str
}

// 检查是否是手机号
func CheckMobile(mobile string) bool {
	r := regexp.MustCompile(`^1[0-9]{10}$`)
	return r.Match([]byte(mobile))
}

func NewWebStuckTitle() *taxerr.SystemErr {
	return taxerr.NewSystemV3("电子税局页面卡顿", "系统将于30分钟后重试(可在\"通用设置\"关闭)")
}

// 查看设备是否在线
func CheckEqmLive(tel string) bool {
	client := lxhttp.NewHttpClient()
	defer client.CloseIdleConnections()
	bys, err := lxhttp.POSTJson(client, "https://keep.jsptax.com"+"/api/v1/telDeviceStatus", map[string]string{"mobiles": tel}, map[string]string{})
	if err != nil {
		return false
	}
	if gjson.GetBytes(bys, `data.status`).Bool() {
		return true
	}
	return false
}

func GetData(key variable.SbKey, data string, target interface{}) bool {
	str := gjson.Get(data, string(key)).String()
	if err := MapToStruct(str, target); err != nil {
		logger.Info(err)
		return false
	}
	return true
}

// target 传指针
func MapToStruct(source interface{}, target interface{}) error {
	resByre := []byte("")
	var resByteErr error
	if reflect.TypeOf(source).String() == "string" {
		str := source.(string)
		resByre = []byte(str)
	} else {
		resByre, resByteErr = json.Marshal(source)
		if resByteErr != nil {
			return resByteErr
		}
	}
	err := json.Unmarshal(resByre, target)
	if err != nil {
		return err
	}
	return nil
}

// CancelLeftTasks 撤销其他的任务，避免一直发验证码导致超限，仅新版登录使用
func CancelLeftTasks(tel string) {
	c := lxhttp.NewHttpClient()
	lxhttp.POSTJson(c, variable.TaxTaskURL+"/api/v1/stopQueue", map[string]string{"tel": tel}, nil)
}

// GetTelTaskStatus 如果有任务正在进行 返回 true
func GetTelTaskStatus(tel string) (bool, error) {
	c := lxhttp.NewHttpClient()
	bys, err := lxhttp.POSTJson(c, `https://task.listensoft.net/api/v1/other/telTaskDoi`, map[string]string{
		"tel": tel,
	}, nil)
	if err != nil {
		return false, err
	}
	if gjson.GetBytes(bys, `data.res`).Bool() {
		return true, nil
	} else {
		return false, nil
	}
}

var rng = rand.New(rand.NewSource(time.Now().UnixMicro()))

// GenTracks 函数生成移动轨迹。先做加速运动，再做减速运动。
//
//	设滑块滑动的加速度为 a；当前速度为 v；初速度为 v0；位移为 x；所需时间为 t。
//	根据加速度物理公式，它们满足以下关系：
//	v = v0 + a * t
//	x = v0 * t + 0.5 * a * t * t
//
// 参考：
//
// https://python3webspider.cuiqingcai.com/8.2-ji-yan-hua-dong-yan-zheng-ma-shi-bie
func GenTracks(distance float64) []float64 {
	var track = make([]float64, 0)     // 移动轨迹
	var current float64 = 0            // 当前位移
	var mid float64 = distance * 4 / 5 // 减速阈值
	var t float64 = 0.15               // 计算间隔
	var v float64 = 0                  // 初速度
	var a1 = rng.Float64()*6 + 2       // 正加速度 [2,8)
	var a2 = -a1 - 2                   // 负加速度 (-10,-4]

	for current < distance {
		// 加速度
		var a float64
		if current < mid {
			a = a1
		} else {
			a = -a2
		}
		// 初速度 v0
		v0 := v
		// 当前速度 v = v0 + at
		v = v0 + a*t
		// 移动距离 x = v0t + 1/2 * a * t^2
		x := v0*t + 1.0/2.0*a*t*t

		current += x
		track = append(track, x)
	}

	// 修正超标值
	track = append(track, distance-current)
	return track
}

func Trim(str interface{}) string {
	return strings.TrimSpace(fmt.Sprintf("%s", str))
}

// 生成随机数
func RandSeq(n int) string {
	var letters = []rune("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}
