package sso

import (
	"context"
	"dubbo.apache.org/dubbo-go/v3/common/logger"
	"encoding/json"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/fonchain_enterprise/fonchain-main/api/account"
	"github.com/fonchain_enterprise/fonchain-main/pkg/cache"
	"github.com/fonchain_enterprise/fonchain-main/pkg/config"
	"github.com/fonchain_enterprise/fonchain-main/pkg/e"
	"github.com/fonchain_enterprise/fonchain-main/pkg/service"
	"github.com/fonchain_enterprise/fonchain-main/pkg/utils/secret"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/google/uuid"
	"io/ioutil"
	"net/http"
	"reflect"
	"strings"
	"time"
)

var (
	issuer                = "https://erp.fontree.cn"
	authorizationEndpoint = "https://common.szjixun.cn/sso/auth"
	tokenEndpoint         = "https://common.szjixun.cn/sso/token"
	userinfoEndpoint      = "https://common.szjixun.cn/sso/userinfo"
	jwksUri               = "https://common.szjixun.cn/sso/.well-known/jwks.json"
)

type ApplicationInfo struct {
	ClientId     string `json:"clientId"`
	ClientSecret string `json:"clientSecret"`
}

type UserInfo struct {
	Sub               string `json:"sub"`
	Name              string `json:"name"`
	GivenName         string `json:"given_name"`
	FamilyName        string `json:"family_name"`
	PreferredUsername string `json:"preferred_username"`
	Email             string `json:"email"`
	EmailVerified     bool   `json:"email_verified"`
	Picture           string `json:"picture"`
	// ...其他字段...
}

func LoadEnv() {
	issuer = config.ApiHost
	authorizationEndpoint = issuer + "/sso/auth"
	tokenEndpoint = issuer + "/sso/token"
	userinfoEndpoint = issuer + "/sso/userinfo"
	jwksUri = issuer + "/sso/.well-known/jwks.json"

}

func Configuration(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"issuer":                 issuer,
		"authorization_endpoint": authorizationEndpoint,
		"token_endpoint":         tokenEndpoint,
		"userinfo_endpoint":      userinfoEndpoint,
		"jwks_uri":               jwksUri,
		// 你可以添加其他必要的OIDC配置项
	})
	return
}

//Auth erp授权 如果没有登陆则跳转登陆的页面
func Auth(c *gin.Context) {
	// 验证用户登录,并重定向到回调地址,带上授权码code
	// 这里需要开发者实现用户认证逻辑,并生成授权码
	//authCode := "your_generated_auth_code"
	//db[authCode] = "asdkfljoqeruowerql"
	//获取cookie 解析
	token, err := c.Cookie("token")

	//次数应该有一个查找,但是我暂时不需要
	domainClientId := c.Query("client_id")
	clientKey := cache.GetSsoClientId(domainClientId)

	domainClientSecret := cache.RedisClient.Get(clientKey).Val()
	if domainClientSecret == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "client_id not exist"})
		return
	}

	fmt.Println("授权页面", token, err)
	if err != nil {
		c.Redirect(http.StatusFound, "/sso/login?"+c.Request.URL.RawQuery)
		return
	}

	key := cache.GetSsoAuthHtml()
	exists := cache.RedisClient.Exists(key).Val()

	if exists != 1 {
		b, err := ioutil.ReadFile("./data/static/auth.html")

		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		fmt.Println(reflect.TypeOf(b))

		cache.RedisClient.Set(key, string(b), 300*time.Second)
	}

	htmlContent := cache.RedisClient.Get(key).Val()

	c.Writer.WriteHeader(http.StatusOK)
	c.Writer.Write([]byte(htmlContent))

	return
}

//AuthSuccess 授权通过
func AuthSuccess(c *gin.Context) {
	// 验证用户登录,并重定向到回调地址,带上授权码code
	// 这里需要开发者实现用户认证逻辑,并生成授权码
	//authCode := "your_generated_auth_code"
	//db[authCode] = "asdkfljoqeruowerql"
	//获取cookie 解析
	token, err := c.Cookie("token")
	domainClientId := c.Query("client_id")
	clientKey := cache.GetSsoClientId(domainClientId)

	domainClientSecret := cache.RedisClient.Get(clientKey).Val()
	if domainClientSecret == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "client_id not exist"})
		return
	}

	if err != nil {
		//跳转到登陆
		c.Redirect(http.StatusFound, "/sso/login?"+c.Request.URL.RawQuery)
		return
	}

	authCode, b := genJwt(token)

	if b != true {
		c.Redirect(http.StatusFound, "/sso/login?"+c.Request.URL.RawQuery)
		return
	}

	applicationInfo := ApplicationInfo{
		ClientId:     domainClientId,
		ClientSecret: domainClientSecret,
	}

	appByte, err := json.Marshal(applicationInfo)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	cache.RedisClient.Set(cache.GetSSOCodeApplication(authCode), string(appByte), 1200*time.Second)

	c.Redirect(http.StatusFound, c.Query("redirect_uri")+"?code="+authCode+"&state="+c.Query("state"))
}

func Token(c *gin.Context) {

	fmt.Println("令牌断电")
	var appInfo *ApplicationInfo
	code := c.PostForm("code")
	fmt.Println("code-------", code)

	fmt.Println("Body:", c.PostForm("code"))
	fmt.Println("Body:", c.PostForm("grant_type"))
	fmt.Println("Body:", c.PostForm("redirect_uri"))

	if code == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "code is nil"})
		return
	}

	fmt.Println("应用信息信息", code, cache.GetSSOCodeApplication(code))
	appStr := cache.RedisClient.Get(cache.GetSSOCodeApplication(code)).Val()

	err := json.Unmarshal([]byte(appStr), &appInfo)
	fmt.Println("应用信息信息", appStr)
	fmt.Println("应用信息信息", appInfo)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	id, err := cache.RedisClient.Get(cache.GetSSOCode(code)).Int64()

	fmt.Println(id, err)
	if err != nil {
		//c.String(500, "redis没有查找到code error: %s", err.Error())
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 打印Body内容
	req := &account.InfoRequest{
		ID: uint64(id),
	}

	info, err := service.AccountProvider.Info(c, req)

	if err != nil {
		//c.String(500, "redis没有查找到code error: %s", err.Error())
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"iss": issuer, // 你的管理系统 URL
		"sub": getAccountNumber(info.Info.Domain, info.Info.ID),
		"aud": appInfo.ClientId, // Gitea OAuth 应用的 client_id
		"exp": time.Now().Add(time.Hour * 72).Unix(),
		"iat": time.Now().Unix(),
	})

	tokenString, err := token.SignedString([]byte(appInfo.ClientSecret)) // 使用你的密钥对其进行签名
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
		return
	}

	b, err := json.Marshal(info.Info)

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	accessToken := uuid.New().String()
	cache.RedisClient.Set(cache.GetSsoAccessToken(accessToken), string(b), 6*time.Hour)

	c.JSON(http.StatusOK, gin.H{
		"access_token": accessToken,
		"token_type":   "bearer",
		"expires_in":   7200,        // 建议加上令牌过期时间
		"id_token":     tokenString, // ID令牌通常包含用户信息的JWT
	})
	return
}

func SsoUserInfo(c *gin.Context) {
	var userInfo account.AccountInfo
	givenName := ""
	familyName := ""

	fmt.Println("用户信息")
	// 校验访问令牌,并返回用户信息
	// 这里需要验证访问令牌是否有效
	accessToken := c.GetHeader("Authorization")
	accessToken = strings.Replace(accessToken, "Bearer ", "", 1)

	fmt.Println(accessToken)
	key := cache.GetSsoAccessToken(accessToken)
	fmt.Println(key)

	b := cache.RedisClient.Get(key).Val()
	fmt.Println("信息是", b)
	if b == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_token"})
		return
	}
	err := json.Unmarshal([]byte(b), &userInfo)

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if userInfo.NickName != "" {
		runes := []rune(userInfo.NickName)
		familyName = string(runes[0])
		givenName = string(runes[1:])
	}

	info := UserInfo{
		Sub:               getAccountNumber(userInfo.Domain, userInfo.ID),
		Name:              userInfo.NickName,
		GivenName:         familyName,
		FamilyName:        givenName,
		PreferredUsername: userInfo.EnglishName,
		Email:             userInfo.MailAccount,
		EmailVerified:     true,
		Picture:           userInfo.Avatar,
	}

	/*
		userInfo := UserInfo{
			Sub:               "248289761001",
			Name:              "Jane Doe",
			GivenName:         "Jane",
			FamilyName:        "Doe",
			PreferredUsername: "j.doe",
			Email:             "janedoe@example.com",
			EmailVerified:     true,
			Picture:           "http://example.com/janedoe/me.jpg",
		}
	*/
	c.JSON(http.StatusOK, info)
	return
}

func LoginHtml(c *gin.Context) {

	exists := cache.RedisClient.Exists(cache.GetSSoLoginHtml()).Val()
	if exists != 1 {
		b, err := ioutil.ReadFile("./data/static/sso.html")

		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		cache.RedisClient.Set(cache.GetSSoLoginHtml(), string(b), 300*time.Second)
	}

	htmlContent := cache.RedisClient.Get(cache.GetSSoLoginHtml()).Val()

	c.Writer.WriteHeader(http.StatusOK)
	c.Writer.Write([]byte(htmlContent))

	return
}

func Login(c *gin.Context) {

	var req account.LoginRequest
	if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil {
		service.Error(c, e.InvalidParams, err)
		return
	}
	req.Ip = c.ClientIP()

	res, err := service.AccountProvider.Login(c, &req)

	if err != nil {
		service.Error(c, e.InvalidParams, err)
		return
	}

	token, err := secret.CombineSecret(res.Token, "zz", res.Token)

	if err != nil {
		service.Error(c, e.Error, err)
		return
	}

	c.SetCookie("token", token, 43200, "/", "", false, true)

	//c.Redirect(http.StatusFound, c.Query("redirect_uri")+"?code="+c.Query("code")+"&state="+c.Query("state"))
	service.Success(c, "")

	return
}

func genJwt(token string) (string, bool) {

	//解析token
	jwtToken, err := secret.GetJwtFromStr(token)
	if err != nil {
		return "'", false
	}

	req := account.DecryptJwtRequest{
		Token: jwtToken,
	}

	info, err := service.AccountProvider.DecryptJwt(context.Background(), &req) // 处理

	if err != nil {
		logger.Warn("sso 解密微服务提示错误", err)
		return "", false
	}

	//info.ID =
	//生成 uuid 插入redis
	code := uuid.New().String()
	cache.RedisClient.Set(cache.GetSSOCode(code), info.ID, 600*time.Second)

	return code, true
}

func getAccountNumber(domain string, id uint64) string {
	return fmt.Sprintf("%s_%010d", domain, id)
}