package handler

import (
	_ "embed"
	"errors"
	"github.com/google/uuid"
	"github.com/loveuer/nf"
	"github.com/loveuer/nf/nft/log"
	"github.com/loveuer/nf/nft/resp"
	"gorm.io/gorm"
	"net/http"
	"net/url"
	"time"
	"uauth/internal/store/cache"
	"uauth/internal/store/db"
	"uauth/model"
)

var (
	//go:embed serve_authorize.html
	pageAuthorize string
)

func Authorize(c *nf.Ctx) error {

	type Req struct {
		ClientId     string `query:"client_id"`
		ResponseType string `query:"response_type"`
		RedirectURI  string `query:"redirect_uri"`
		Scope        string `query:"scope"`
		State        string `query:"state"`
	}

	var (
		ok         bool
		op         *model.User
		req        = new(Req)
		err        error
		client     = &model.Client{}
		authRecord = &model.AuthorizationRecord{}
		uri        *url.URL
	)

	if err = c.QueryParser(req); err != nil {
		log.Error("[S] query parser err = %s", err.Error())
		return c.Status(http.StatusBadRequest).SendString("Invalid request")
	}

	if req.ResponseType != "code" {
		log.Warn("[S] response type = %s", req.ResponseType)
		return c.Status(http.StatusBadRequest).SendString("Invalid request")
	}

	// 如果未登录,则跳转到登录界面
	if op, ok = c.Locals("user").(*model.User); !ok {
		log.Info("[S] op not logined, redirect to login page")
		return c.Redirect("/oauth/v2/login?"+c.Request.URL.Query().Encode(), http.StatusFound)
	}

	log.Info("[S] Authorize: username = %s, client_id = %s", op.Username, req.ClientId)

	if err = db.Default.Session().Where("client_id", req.ClientId).Take(client).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid client_id")
		}

		log.Error("[Authorize]: db take clients err = %s", err.Error())
		return c.Status(http.StatusInternalServerError).SendString("Internal Server Error")
	}

	if err = db.Default.Session().Model(&model.AuthorizationRecord{}).
		Where("user_id", op.Id).
		Where("client_id", client.Id).
		Take(authRecord).
		Error; err != nil {
		// 用户第一次对该 client 进行授权
		if errors.Is(err, gorm.ErrRecordNotFound) {

			return c.RenderHTML("authorize", pageAuthorize, map[string]any{
				"user": map[string]any{
					"username": op.Username,
					"avatar":   "https://picsum.photos/200",
				},
				"client_id":    req.ClientId,
				"redirect_uri": req.RedirectURI,
				"scope":        req.Scope,
				"state":        req.State,
			})
		}

		log.Error("[Authorize]: db take authorization_records err = %s", err.Error())
		return resp.Resp500(c, err)
	}

	// 当用户已经授权过时
	// 生成授权码并缓存授权码
	log.Debug("[Authorize]: username = %s already approved %s", op.Username, client.Name)

	authorizationCode := uuid.New().String()[:8]
	if err = cache.Client.SetEx(c.Context(), cache.Prefix+"auth_code:"+authorizationCode, op.Id, 10*time.Minute); err != nil {
		return resp.Resp500(c, err)
	}

	if uri, err = url.Parse(req.RedirectURI); err != nil {
		log.Warn("[S] parse redirect uri = %s, err = %s", req.RedirectURI, err.Error())
		return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid redirect uri")
	}

	qs := uri.Query()
	qs.Add("code", authorizationCode)
	qs.Add("client_id", req.ClientId)
	qs.Add("scope", req.Scope)
	qs.Add("state", req.State)

	uri.ForceQuery = true
	value := uri.String() + qs.Encode()

	return c.RenderHTML("approve", pageApprove, map[string]interface{}{
		"redirect_uri": value,
	})
}