package outServer

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strings"
	"sync"
	"time"

	"app.yhyue.com/moapp/jybase/common"
	"bp.jydev.jianyu360.cn/BaseService/gateway/core/router"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/util/gconv"
	"golang.org/x/net/publicsuffix"
)

type sussBi struct {
	addr           string
	loginAddr      string
	user           string
	pwd            string
	Url            *url.URL
	cookiePath     string
	lastLogin      time.Time
	loginLock      *sync.RWMutex
	jar            *cookiejar.Jar
	succbiJar      *cookiejar.Jar
	prm            *ParamReplaceManager
	singlePointUrl *regexp.Regexp
}

// 参数替换
type ParamReplace struct {
	Replace []ParamReplaceSetting
	Match   []ParamReplaceSetting
}

type ParamReplaceSetting struct {
	Key, Value string
}

type ParamReplaceManager struct {
	eqRouters   map[string]*ParamReplace
	regexRouter map[*regexp.Regexp]*ParamReplace
}

func InitSussBi(config map[string]interface{}) (*sussBi, error) {
	address := gconv.String(config["addr"])
	user := gconv.String(config["user"])
	password := gconv.String(config["password"])
	paramReplace := gconv.Map(config["paramReplace"])
	cookiePath := gconv.String(config["cookiePath"])
	loginAddr := gconv.String(config["loginAddr"])
	if address == "" {
		return nil, fmt.Errorf("配置异常")
	}
	sussCookie, err := cookiejar.New(&cookiejar.Options{
		PublicSuffixList: publicsuffix.List,
	})
	if err != nil {
		return nil, fmt.Errorf("初始化cookie异常")
	}
	sussCookie2, err2 := cookiejar.New(&cookiejar.Options{
		PublicSuffixList: publicsuffix.List,
	})
	if err2 != nil {
		return nil, fmt.Errorf("初始化cookie异常")
	}
	prManager := &ParamReplaceManager{
		eqRouters:   map[string]*ParamReplace{},
		regexRouter: map[*regexp.Regexp]*ParamReplace{},
	}

	for url, setting := range paramReplace {
		pr := &ParamReplace{}
		settingMap := gconv.Map(setting)
		if settingMap == nil || len(settingMap) == 0 {
			continue
		}
		if replaceMap := gconv.Map(settingMap["replace"]); replaceMap != nil && len(replaceMap) > 0 {
			for k, v := range replaceMap {
				pr.Replace = append(pr.Replace, ParamReplaceSetting{
					Key:   k,
					Value: gconv.String(v),
				})
			}
		}
		replaceMap := gconv.Map(settingMap["match"])
		if replaceMap != nil && len(replaceMap) > 0 {
			for k, v := range replaceMap {
				pr.Match = append(pr.Match, ParamReplaceSetting{
					Key:   k,
					Value: gconv.String(v),
				})
			}
		}
		if len(pr.Match) == 0 && len(pr.Replace) == 0 {
			continue
		}
		if regexp.QuoteMeta(url) == url {
			prManager.eqRouters[url] = pr
		} else {
			if reg, err := regexp.Compile(url); err == nil {
				prManager.regexRouter[reg] = pr
			}
		}
	}
	u, _ := url.Parse(address)
	return &sussBi{
		addr:           address,
		user:           user,
		Url:            u,
		pwd:            password,
		loginAddr:      loginAddr,
		cookiePath:     cookiePath,
		jar:            sussCookie,
		lastLogin:      time.Now(),
		loginLock:      &sync.RWMutex{},
		succbiJar:      sussCookie2,
		prm:            prManager,
		singlePointUrl: regexp.MustCompile(gconv.String(config["singlePointUrl"])),
	}, nil
}

// AutoLogin 自动登录
func (s *sussBi) AutoLogin() error {
	s.loginLock.Lock()
	defer s.loginLock.Unlock()
	if time.Now().Sub(s.lastLogin).Seconds() > 60 {
		client := &http.Client{
			Jar: s.succbiJar,
		}
		resp, err := client.Get(fmt.Sprintf("%s/succbi/?:user=%s&:password=%s", s.addr, s.user, s.pwd))
		defer resp.Body.Close()
		if err != nil {
			return err
		}
		if !(resp.StatusCode == 302 || resp.StatusCode == 200) {
			return fmt.Errorf("自动登录异常")
		}
		s.succbiJar = s.jar
		s.lastLogin = time.Now()
	}
	return nil
}

// RequestLogin 装配登录状态
func (s *sussBi) RequestLogin(r *ghttp.Request) error {
	if s.singlePointUrl.MatchString(r.RequestURI) {
		ctx := router.GetGContext(r.GetCtx())
		md5Val := common.GetMd5String(fmt.Sprintf("%s_%s_%d_%d_%d_%d_%d_%d_%s_%d_%s_%d", ctx.Sess.NickName, ctx.Sess.YyName, ctx.Sess.EntRole, ctx.Sess.EntNicheDis, ctx.Sess.PositionId, ctx.Sess.AccountId, ctx.Sess.EntAccountId, ctx.Sess.EntId, ctx.Sess.EntName, ctx.Sess.EntDeptId, ctx.Sess.EntUserName, ctx.Sess.EntUserId))
		c := &http.Cookie{
			Name:     "BITOKEN",
			Value:    md5Val,
			Path:     "/",
			HttpOnly: false,
			MaxAge:   604800,
			Expires:  time.Now().AddDate(0, 0, 7),
		}
		r.Request.AddCookie(c)
		log.Println(ctx.Sess.PositionId, "BITOKEN====", md5Val)
		//http.SetCookie(r.Response.ResponseWriter, c)
	} else {
		if strings.HasPrefix(r.URL.Path, "/succbi") {
			u, e := url.Parse(s.Url.String() + "/succbi")
			if e != nil {
				log.Println("RequestLogin url.Parse error", e)
				return e
			}
			log.Println("succbiJar----", u.Path, u.RequestURI(), s.succbiJar.Cookies(u))
			if cookies := s.succbiJar.Cookies(u); len(cookies) > 0 {
				log.Println("RequestLogin AddCookie", cookies[0])
				r.Request.AddCookie(cookies[0])
			}
		} else {
			if cookies := s.jar.Cookies(s.Url); len(cookies) > 0 {
				r.Request.AddCookie(cookies[0])
			}
		}
	}
	return nil
}

// CheckLoginOut 检测登录状态是否过期
func (s *sussBi) CheckLoginOut(r *ghttp.Request) bool {
	if r.Response.Status == 401 {
		return true
	}
	return false
}

func (s *sussBi) Filter(r *ghttp.Request) error {
	ctx := router.GetGContext(r.GetCtx())
	if ctx.Sess.NewUid != 0 {
		replaceMap := map[string]interface{}{
			"jyUserId":         ctx.Sess.PositionId,
			"jyUserPositionId": ctx.Sess.PositionId,
			"jyUserAccountId":  ctx.Sess.AccountId,
			"jyEntPositionId":  ctx.Sess.PositionId,
			"jyEntAccountId":   ctx.Sess.EntAccountId,
			"jyUserName":       ctx.Sess.UserName,
			"jyEntName":        ctx.Sess.EntName,
			"jyEntId":          ctx.Sess.EntId,
			"jyEntUserName":    ctx.Sess.EntUserName,
			"jyEntUserId":      ctx.Sess.EntUserId,
		}
		if r.Request.Method == http.MethodPost {
			bodyBytes, err := io.ReadAll(r.Request.Body)
			if err != nil {
				return err
			}
			if len(bodyBytes) > 0 {
				finalBytes := bodyBytes
				for k, v := range replaceMap {
					finalBytes = bytes.ReplaceAll(finalBytes, []byte(`"`+k+`"`), []byte(`"`+fmt.Sprint(v)+`"`))
				}
				r.ContentLength = gconv.Int64(len(finalBytes))
				r.Request.Header.Set("Content-Length", fmt.Sprintf("%d", len(finalBytes)))
				r.Request.Body = ioutil.NopCloser(bytes.NewReader(finalBytes))
			}
		} else if r.Request.Method == http.MethodGet {
			var prArr *ParamReplace
			if rule, ok := s.prm.eqRouters[r.URL.Path]; ok && rule != nil {
				prArr = rule
			} else {
				for reg, rule := range s.prm.regexRouter {
					if reg.MatchString(r.URL.Path) {
						prArr = rule
						break
					}
				}
			}
			if prArr != nil {
				if newValues, err := url.ParseQuery(r.URL.RawQuery); err == nil && len(newValues) > 0 {
					for _, replace := range prArr.Replace {
						if replace.Key != "" && replace.Value != "" && replaceMap[replace.Value] != nil {
							newValues[replace.Key] = []string{fmt.Sprintf("%v", replaceMap[replace.Value])}
						}
					}
					for _, match := range prArr.Match {
						if match.Key != "" && match.Value != "" {
							if arr := strings.Split(match.Key, "."); len(arr) == 2 && replaceMap[match.Value] != nil {
								if value, ok := newValues[arr[0]]; ok && len(value) > 0 {
									newValue := strings.ReplaceAll(value[0], arr[1], fmt.Sprintf("%v", replaceMap[match.Value]))
									newValues[arr[0]] = []string{newValue}
								}
							}
						}
					}
					r.URL.RawQuery = newValues.Encode()
				}
			}
		}
	}
	return nil
}
