package services

import (
	"code.smartsheep.studio/hydrogen/interactive/pkg/database"
	"code.smartsheep.studio/hydrogen/interactive/pkg/models"
	"code.smartsheep.studio/hydrogen/interactive/pkg/security"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"strconv"
	"time"
)

type PassportUserinfo struct {
	Sub               string `json:"sub"`
	Name              string `json:"name"`
	Email             string `json:"email"`
	Picture           string `json:"picture"`
	PreferredUsername string `json:"preferred_username"`
}

func LinkAccount(userinfo PassportUserinfo) (models.Account, error) {
	id, _ := strconv.Atoi(userinfo.Sub)

	var account models.Account
	if err := database.C.Where(&models.Account{
		ExternalID: uint(id),
	}).First(&account).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			account = models.Account{
				Name:         userinfo.Name,
				Nick:         userinfo.PreferredUsername,
				Avatar:       userinfo.Picture,
				EmailAddress: userinfo.Email,
				PowerLevel:   0,
				ExternalID:   uint(id),
			}
			return account, database.C.Save(&account).Error
		}
		return account, err
	}

	account.Name = userinfo.Name
	account.Nick = userinfo.PreferredUsername
	account.Avatar = userinfo.Picture
	account.EmailAddress = userinfo.Email

	err := database.C.Save(&account).Error

	return account, err
}

func GetToken(account models.Account) (string, string, error) {
	var err error
	var refresh, access string

	sub := strconv.Itoa(int(account.ID))
	access, err = security.EncodeJwt(
		uuid.NewString(),
		security.JwtAccessType,
		sub,
		[]string{"interactive"},
		time.Now().Add(30*time.Minute),
	)
	if err != nil {
		return refresh, access, err
	}
	refresh, err = security.EncodeJwt(
		uuid.NewString(),
		security.JwtRefreshType,
		sub,
		[]string{"interactive"},
		time.Now().Add(30*24*time.Hour),
	)
	if err != nil {
		return refresh, access, err
	}

	return access, refresh, nil
}

func RefreshToken(token string) (string, string, error) {
	parseInt := func(str string) int {
		val, _ := strconv.Atoi(str)
		return val
	}

	var account models.Account
	if claims, err := security.DecodeJwt(token); err != nil {
		return "404", "403", err
	} else if claims.Type != security.JwtRefreshType {
		return "404", "403", fmt.Errorf("invalid token type, expected refresh token")
	} else if err := database.C.Where(models.Account{
		BaseModel: models.BaseModel{ID: uint(parseInt(claims.Subject))},
	}).First(&account).Error; err != nil {
		return "404", "403", err
	}

	return GetToken(account)
}