package common

import (
	"errors"
	"git.listensoft.net/tool/jspkit/common/lxrod"
	"git.listensoft.net/tool/jspkit/logger"
	"git.listensoft.net/tool/jspkit/taxerr"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/input"
	"github.com/go-rod/rod/lib/proto"
	"github.com/go-rod/rod/lib/utils"
	"go.uber.org/zap"
	"strings"
	"time"
)

func MustHas(page *rod.Page, xpath string) bool {
	x, _, _ := HasX(page, xpath)
	return x
}

func MustHasX(page *rod.Page, xpath string) bool {
	x, _, _ := HasX(page, xpath)
	return x
}

func MustHasXV(page *rod.Page, xpath string) bool {
	for _, element := range MustElementsX(page, xpath) {
		visible, _ := element.Visible()
		if visible {
			return true
		}
	}
	return false
}

func MustElementX(page *rod.Page, xpath string) *rod.Element {
	for i := 0; i < 5; i++ {
		x, _ := ElementX(page, xpath)
		if x != nil {
			return x
		}
		utils.Sleep(1)
	}
	return nil
}

func MustHasXV1(page *rod.Page, xpath string) bool {
	for _, element := range MustElementsX1(page, xpath) {
		visible, _ := element.Visible()
		if visible {
			return true
		}
	}
	return false
}

func HasX(page *rod.Page, xpath string) (bool, *rod.Element, error) {
	var x *rod.Element
	var has bool
	var err error
	err = rod.Try(func() {
		has, x, err = page.HasX(xpath)
		if has {
			return
		}
		if err != nil {
			panic(err)
		}
		if has {
			return
		}
		iframe := GetAllIframe(page)
		for _, pg := range iframe {
			has, x, err = pg.HasX(xpath)
			if has {
				return
			}
			if err != nil {
				panic(err)
			}
			if has {
				return
			}
		}
	})
	if x != nil {
		x = x.Timeout(time.Second * 15)
	}
	return has, x, err
}

func ElementX(page *rod.Page, xpath string) (*rod.Element, error) {
	_, element, err := HasX(page, xpath)
	if err != nil {
		err = taxerr.NewWebStuckTitle(false)
	}
	return element, err
}

func GetAllIframe(page *rod.Page) []*rod.Page {
	fs := make([]*rod.Page, 0)
	if page.MustHasX(`//iframe`) {
		for _, element := range page.MustElementsX(`//iframe`) {
			fs = append(fs, element.MustFrame())
			fs = append(fs, GetAllIframe(element.MustFrame())...)
		}
	}
	return fs
}

func MustElementXV(page *rod.Page, xpath string) *rod.Element {
	for i := 0; i < 5; i++ {
		x, _ := ElementsX(page, xpath)
		for _, element := range x {
			visible, _ := element.Visible()
			if visible {
				return element
			}
		}
		utils.Sleep(1)
	}
	return nil
}

func MustElementsX(page *rod.Page, xpath string) (x []*rod.Element) {
	for i := 0; i < 5; i++ {
		x, _ = ElementsX(page, xpath)
		if len(x) != 0 {
			break
		}
		utils.Sleep(1)
	}
	return x
}

func MustElementsX1(page *rod.Page, xpath string) (x []*rod.Element) {
	x, _ = ElementsX(page, xpath)
	return x
}

func ElementsX(page *rod.Page, xpath string) ([]*rod.Element, error) {
	var arr []*rod.Element
	var err error
	err = rod.Try(func() {
		r, _ := page.Timeout(ClickTimeOut).ElementsX(xpath)
		if err != nil {
			panic(err)
		}
		arr = append(arr, r...)
		for _, pg := range GetAllIframe(page) {
			pg.MustWaitLoad()
			r, err = pg.Timeout(ClickTimeOut).ElementsX(xpath)
			if err != nil {
				panic(err)
			}
			arr = append(arr, r...)
		}
	})
	return arr, err
}

func WaitElementX(page *rod.Page, xpath string, interval float64) *rod.Element {
	for i := 0; i < 60; i++ {
		if has, el, _ := HasX(page, xpath); has {
			return el
		}
		time.Sleep(time.Duration(float64(time.Second) * interval))
	}
	return nil
}

func WaitNavigation(page *rod.Page, url string) {
	navigation := page.Timeout(time.Minute).MustWaitNavigation()
	page.Timeout(time.Minute).MustNavigate(url)
	logger.Info("调试埋点11-1")
	utils.Sleep(1)
	WaitTimeOut(navigation, time.Minute)
	logger.Info("埋点11-2")
}

func WaitTimeOut(f func(), timeout time.Duration) {
	resultChan := make(chan struct{}, 1)
	go func() {
		f()
		resultChan <- struct{}{}
	}()
	select {
	case <-resultChan:
		return
	case <-time.After(timeout):
		panic(taxerr.NewWebStuckTitle(ComInfo.Cscsts))
	}
}

func Link(p *rod.Page) {
	go func() {
		//for {
		//utils.Sleep(1)
		if p == nil {
			return
		}
		rod.Try(func() {
			lxrod.DialogWatch(nil, p)
			go p.Browser().EachEvent(func(e *proto.TargetTargetCreated) {
				defer func() {
					if r := recover(); r != nil {
						logger.Error("EachPageCreatedEvent recover:", zap.Any("r", r))
					}
				}()
				if e.TargetInfo.Type != proto.TargetTargetInfoTypePage {
					return
				}
				page := p.Browser().MustPageFromTargetID(e.TargetInfo.TargetID)
				lxrod.DialogWatch(nil, page)
			})()
			//		has, _, _ := p.Has("dialog")
			//		if has {
			//			_, _ = p.Eval(`
			//window.alert = function() {};
			//window.confirm = function() { return true; };
			//window.prompt = function() {};`)
			//		}
		})
		//}
	}()
}

// 有就点 没有就不点击
func MustClick(page *rod.Page, xpath string) {
	x := MustElementsX(page, xpath)
	for _, element := range x {
		visible, _ := element.Visible()
		if visible {
			element.MustClick()
			break
		}
	}
}

func MustText(page *rod.Page, xpath string) string {
	//去掉左右空格
	return strings.TrimSpace(MustElementX(page, xpath).MustText())
}

// 输入文字
func Input(p *rod.Page, selector, inputVal string) {
	err := rod.Try(func() {
		if inputVal == "" || inputVal == "*" {
			inputVal = "0"
		}
		e := rod.Try(func() {
			p.Timeout(time.Second).MustSearch(selector).MustSelectAllText().MustFrame().Keyboard.MustType(input.Backspace)
		})
		if e != nil {
			if w := errors.Unwrap(e); w != nil {
				log.Info(w.Error() + "-" + selector + "-" + inputVal)
			} else {
				log.Info(e.Error() + "-" + selector + "-" + inputVal)
			}
		}
		e = rod.Try(func() {
			p.Timeout(time.Second).MustSearch(selector).MustSelectAllText().MustInput(inputVal)
		})
		if e != nil {
			if w := errors.Unwrap(e); w != nil {
				log.Info(strings.Split(w.Error(), "\t")[0] + "-" + selector + "-" + inputVal)
			} else {
				log.Info(strings.Split(e.Error(), "\t")[0] + "-" + selector + "-" + inputVal)
			}
		}
	})
	if err != nil {
		log.Info(err.Error() + "-" + selector + "-" + inputVal)
	}
}

// 输入文字
func MustInputX(p *rod.Page, xPath, inputVal string) {
	e := MustElementX(p, xPath)
	_ = e.ScrollIntoView()
	e.MustClick().MustSelectAllText().MustInput(inputVal)
}

// 拦截请求
func PageHijackReq(b *rod.Page, pattern string, ch chan []byte) *rod.HijackRouter {
	router := b.HijackRequests()
	router.MustAdd(pattern, func(ctx *rod.Hijack) {
		rod.Try(func() {
			ctx.MustLoadResponse()
			ch <- []byte(ctx.Response.Body())
			utils.Sleep(5)
			select {
			case <-ch:
				break
			}
		})
	})
	go router.Run()
	return router
}

// 获取拦截请求的值
func GetHijackResp(ch chan []byte, t float64) ([]byte, error) {
	d := time.Duration(t * float64(time.Second))
	select {
	case val := <-ch:
		return val, nil
	case <-time.After(d):
		return nil, errors.New("请求网页超时,请稍后重试!")
	}
}