//
// post.go
// Copyright (C) 2025 veypi <i@veypi.com>
// 2025-05-09 17:26
// Distributed under terms of the MIT license.
//

package token

import (
	"net/http"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/veypi/vbase/cfg"
	"github.com/veypi/vbase/libs/auth"
	"github.com/veypi/vbase/models"
	"github.com/veypi/vigo"
	"github.com/veypi/vigo/logv"
)

type postOpts struct {
	// 两种获取token方式，一种用refreshtoken换取apptoken(应用登录)，一种用密码加密code换refreshtoken (oa登录)
	Refresh *string `json:"refresh"  src:"json" desc:"刷新令牌"`
	Typ     *string `json:"typ"  src:"json" desc:"令牌类型"`

	AppID     *string    `json:"app_id" gorm:"index;type:varchar(36)"  src:"json" desc:"应用ID"`
	ExpiredAt *time.Time `json:"expired_at"  src:"json" desc:"过期时间"`
	OverPerm  *string    `json:"over_perm"  src:"json" desc:"覆盖权限"`
	Device    *string    `json:"device"  src:"json" desc:"设备信息"`
}

var _ = Router.Post("/", "创建 Token", vigo.SkipBefore, tokenPost)

// for user login app
func tokenPost(x *vigo.X, opts *postOpts) (string, error) {
	aid := cfg.Config.ID
	if opts.AppID != nil && *opts.AppID != "" {
		aid = *opts.AppID
	}
	data := &models.Token{}
	claim := &auth.Claims{}
	claim.IssuedAt = jwt.NewNumericDate(time.Now())
	claim.Issuer = cfg.Config.ID
	if opts.Refresh != nil {
		typ := "app"
		if opts.Typ != nil {
			typ = *opts.Typ
		}
		// for other app redirect
		refresh, err := auth.ParseJwt(*opts.Refresh)
		if err != nil {
			return "", err
		}
		if refresh.ID == "" {
			return "", vigo.ErrNotAuthorized
		}
		err = cfg.DB().Where("id = ?", refresh.ID).First(data).Error
		if err != nil {
			return "", err
		}
		claim.AID = aid
		claim.UID = refresh.UID
		claim.Name = refresh.Name
		claim.Icon = refresh.Icon
		claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(cfg.Config.TokenExpire))
		if typ == "app" {
			if refresh.AID == aid {
				// refresh token
				acList := make(auth.Access, 0, 10)
				logv.AssertError(cfg.DB().Table("accesses a").
					Select("a.name, a.t_id, a.level").
					Joins("INNER JOIN user_roles ur ON ur.role_id = a.role_id AND ur.user_id = ? AND a.app_id = ?", refresh.UID, aid).
					Scan(&acList).Error)
				claim.Access = acList
				if aid == cfg.Config.ID {
					return auth.GenJwt(claim)
				}
				app := &models.App{}
				err = cfg.DB().Where("id = ?", aid).First(app).Error
				if err != nil {
					return "", err
				}
				return auth.GenJwtWithKey(claim, app.Key)
			} else if refresh.ID == cfg.Config.ID {
				// oa应用生成其他应用的refresh token
				newToken := &models.Token{}
				newToken.UserID = refresh.UID
				newToken.AppID = aid
				newToken.ExpiredAt = time.Now().Add(time.Hour * 24)
				if opts.OverPerm != nil {
					newToken.OverPerm = *opts.OverPerm
				}
				if opts.Device != nil {
					newToken.Device = *opts.Device
				}
				newToken.Ip = x.GetRemoteIP()
				logv.AssertError(cfg.DB().Create(newToken).Error)
				// gen other app token
				claim.ID = newToken.ID
				claim.ExpiresAt = jwt.NewNumericDate(newToken.ExpiredAt)
				return auth.GenJwt(claim)
			} else {
				return "", vigo.ErrNotPermitted
			}
		} else if typ == "ufs" {
			claim.AID = refresh.AID
			claim.UID = refresh.UID
			claim.Name = refresh.Name
			claim.Icon = refresh.Icon
			claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(cfg.Config.TokenExpire))
			claim.Access = auth.Access{
				{Name: "fs", TID: "/", Level: auth.Do},
			}
			token := logv.AssertFuncErr(auth.GenJwt(claim))

			cookie := &http.Cookie{
				Name:     "fstoken", // Cookie 的名称
				Value:    token,     // Cookie 的值
				Path:     "/fs/u/",  // Cookie 的路径，通常是根路径
				MaxAge:   600,       // Cookie 的最大年龄，单位是秒
				HttpOnly: true,      // 是否仅限 HTTP(S) 访问
				Secure:   false,     // 是否通过安全连接传输 Cookie
			}
			http.SetCookie(x.ResponseWriter(), cookie)
			return token, nil
		} else {
			return "", vigo.ErrArgInvalid
		}
	} else {
		return "", vigo.ErrArgInvalid
	}
}
