package middleware

import (
	"cmdb-backend/pkg/app/request"
	"cmdb-backend/pkg/config"
	"cmdb-backend/pkg/logging"
	"cmdb-backend/pkg/utils"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
)

func Authorize(c *gin.Context) {
	if strings.HasPrefix(c.Request.URL.Path, "/api/account") {
		c.Next()
		return
	}
	if strings.HasPrefix(c.Request.URL.Path, "/check_health") {
		c.Next()
		return
	}
	loginAt := sessions.Default(c).Get("login_at")
	if loginAt == nil || time.Since(time.Unix(loginAt.(int64), 0)) > time.Hour*24 {
		c.Redirect(http.StatusTemporaryRedirect, "/api/account/login")
		c.Abort()
		return
	}

	c.Next()
}

func Login(c *gin.Context) {
	c.Redirect(http.StatusTemporaryRedirect,
		fmt.Sprintf("%s/auth?%s", config.GC.Authing.AppProviderUri, url.Values{
			"response_type": {"code"},
			"client_id":     {config.GC.Authing.AppId},
			"redirect_uri":  {config.GC.Authing.AppRedirectUri},
			"scope":         {"openid profile offline_access phone email"},
			"state":         {utils.RandStringBytes(10)},
		}.Encode()))
}

func Callback(c *gin.Context) {
	resp, err := request.Post(c,
		fmt.Sprintf("%s/token", config.GC.Authing.AppProviderUri),
		request.MapForm{
			"code":          c.Query("code"),
			"grant_type":    "authorization_code",
			"client_id":     config.GC.Authing.AppId,
			"client_secret": config.GC.Authing.AppSecret,
			"redirect_uri":  config.GC.Authing.AppRedirectUri,
		},
		request.Headers{
			"Content-Type": "application/x-www-form-urlencoded",
		})

	if resp != nil && resp.Response.StatusCode >= http.StatusBadRequest {
		err = fmt.Errorf("%s %s", resp.Response.Status, resp.String())
	}
	if err != nil {
		fmt.Println(err)
		logging.Error("get authing token err ", err)
		utils.AmisError(c, err)
		return
	}

	info := &struct {
		IDToken      string `json:"id_token"`
		AccessToken  string `json:"access_token"`
		RefreshToken string `json:"refresh_token"`
	}{}
	err = resp.ToJSON(info)

	if err != nil {
		utils.AmisError(c, err)
		return
	}

	resp, err = request.Get(c,
		fmt.Sprintf("%s/me?access_token=%s", config.GC.Authing.AppProviderUri, info.AccessToken),
	)

	if resp != nil && resp.Response.StatusCode >= http.StatusBadRequest {
		err = fmt.Errorf("%s %s", resp.Response.Status, resp.String())
	}
	if err != nil {
		logging.Error("get authing user info err ", err)
		utils.AmisError(c, err)
		return
	}

	user := &struct {
		Sub   string `json:"sub"`
		Name  string `json:"name"`
		Email string `json:"email"`
		Phone string `json:"phone"`
	}{}

	err = resp.ToJSON(user)
	if err != nil {
		utils.AmisError(c, err)
		return
	}

	session := sessions.Default(c)
	session.Clear()
	session.Set("name", user.Name)
	session.Set("email", user.Email)
	session.Set("phone", user.Phone)
	session.Set("user_id", user.Sub)
	session.Set("login_at", time.Now().Unix())
	err = session.Save()
	if err != nil {
		utils.AmisError(c, err)
		return
	}

	c.Redirect(http.StatusTemporaryRedirect, "/")
}

func Logout(c *gin.Context) {
	session := sessions.Default(c)
	session.Clear()
	err := session.Save()
	if err != nil {
		utils.AmisError(c, err)
		return
	}

	c.Redirect(http.StatusTemporaryRedirect, config.GC.Authing.AppLogOutUri)
}

func Profile(c *gin.Context) {
	session := sessions.Default(c)

	c.JSON(http.StatusOK, gin.H{
		"name":     session.Get("name"),
		"login_at": session.Get("login_at"),
	})
}
