package app

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/veypi/vbase/cfg"
	"github.com/veypi/vbase/libs/auth"
	"github.com/veypi/vbase/libs/utils"
	"github.com/veypi/vbase/models"
	"github.com/veypi/vigo"
	"gorm.io/gorm"
)

type appIDReq struct {
	AppID string `src:"path@app_id" desc:"应用ID"`
}

var _ = Router.Get("/{app_id}/key", "重置应用密钥", auth.Check("app", "app_id", auth.DoDelete), appKey)

func appKey(x *vigo.X, req *appIDReq) (string, error) {
	data := &models.App{}
	data.ID = req.AppID
	key := utils.RandSeq(32)
	err := cfg.DB().Model(data).Update("key", key).Error

	return key, err
}

var _ = Router.Delete("/{app_id}", "删除应用", auth.Check("app", "app_id", auth.DoDelete), appDelete)

func appDelete(x *vigo.X, req *appIDReq) (*models.App, error) {
	data := &models.App{}

	err := cfg.DB().Where("id = ?", req.AppID).Delete(data).Error

	return data, err
}

var _ = Router.Get("/{app_id}", "获取应用详情", auth.Check("app", "app_id", auth.DoRead), appGet)

func appGet(x *vigo.X, req *appIDReq) (*models.App, error) {
	data := &models.App{}
	err := cfg.DB().Where("id = ?", req.AppID).First(data).Error

	return data, err
}

type listOpts struct {
	Name *string `json:"name"  src:"query" desc:"应用名称"`
}

var _ = Router.Get("/", "获取应用列表", appList)

type appListItem struct {
	models.App
	UserStatus string `json:"user_status"`
}

func appList(x *vigo.X, opts *listOpts) ([]*appListItem, error) {
	data := make([]*appListItem, 0, 10)
	token, err := auth.CheckJWT(x)
	if err == nil {
		uid := token.UID
		query := cfg.DB().Table("apps").Select("apps.*,app_users.status user_status")
		if opts.Name != nil {
			query = query.Joins("LEFT JOIN app_users ON app_users.app_id = apps.id AND app_users.user_id = ? AND apps.name LIKE ?", uid, opts.Name)
		} else {
			query = query.Joins("LEFT JOIN app_users ON app_users.app_id = apps.id AND app_users.user_id = ?", uid)
		}
		err = query.Scan(&data).Error
	} else {
		err = cfg.DB().Table("apps").Select("id", "name", "icon").Find(&data).Error
	}
	return data, err
}

type postOpts struct {
	Name    string  `json:"name"  src:"json" desc:"应用名称"`
	Icon    string  `json:"icon"  src:"json" desc:"图标"`
	Des     *string `json:"des"  src:"json" desc:"描述"`
	Typ     string  `json:"typ"  src:"json" desc:"类型"`
	Status  string  `json:"status"  src:"json" desc:"状态"`
	InitUrl string  `json:"init_url" src:"json" desc:"初始化URL"`
}

var _ = Router.Post("/", "创建应用", auth.Check("app", "", auth.DoCreate), appPost)

func appPost(x *vigo.X, opts *postOpts) (*models.App, error) {
	data := &models.App{}
	data.Name = opts.Name
	data.Icon = opts.Icon
	if data.Icon == "" {
		data.Icon = fmt.Sprintf("https://public.veypi.com/img/avatar/%04d.jpg", rand.New(rand.NewSource(time.Now().UnixNano())).Intn(220))
	}
	data.Des = ""
	if opts.Des != nil {
		data.Des = *opts.Des
	}
	data.Typ = opts.Typ
	data.Status = opts.Status
	data.InitUrl = opts.InitUrl
	err := cfg.DB().Transaction(func(tx *gorm.DB) error {
		data.Key = utils.RandSeq(32)
		err := tx.Create(data).Error
		if err != nil {
			return err
		}
		uid, _ := x.Get("uid").(string)
		au := &models.AppUser{
			AppID:  data.ID,
			UserID: uid,
			Status: models.AUSTATUS_OK,
		}
		return tx.Create(au).Error
	})

	return data, err
}

type patchOpts struct {
	ID         string  `json:"id" gorm:"primaryKey;type:varchar(36)"  src:"path@app_id" desc:"应用ID"`
	Name       *string `json:"name"  src:"json" desc:"应用名称"`
	Icon       *string `json:"icon"  src:"json" desc:"图标"`
	Des        *string `json:"des"  src:"json" desc:"描述"`
	Typ        *string `json:"typ" gorm:"default:auto"  src:"json" desc:"类型"`
	InitRoleID *string `json:"init_role_id" gorm:"index;type:varchar(36)"  src:"json" desc:"初始角色ID"`
	Status     *string `json:"status" gorm:"default:ok"  src:"json" desc:"状态"`
	InitUrl    *string `json:"init_url"  src:"json" desc:"初始化URL"`
}

var _ = Router.Patch("/{app_id}", "更新应用", auth.Check("app", "app_id", auth.DoUpdate), appPatch)

func appPatch(x *vigo.X, opts *patchOpts) (*models.App, error) {
	data := &models.App{}

	err := cfg.DB().Where("id = ?", opts.ID).First(data).Error
	if err != nil {
		return nil, err
	}
	optsMap := make(map[string]interface{})
	if opts.Name != nil {
		optsMap["name"] = opts.Name
	}
	if opts.Icon != nil {
		optsMap["icon"] = opts.Icon
	}
	if opts.Des != nil {
		optsMap["des"] = opts.Des
	}
	if opts.Typ != nil {
		optsMap["typ"] = opts.Typ
	}
	if opts.InitRoleID != nil {
		optsMap["init_role_id"] = opts.InitRoleID
	}
	if opts.Status != nil {
		optsMap["status"] = opts.Status
	}
	if opts.InitUrl != nil {
		optsMap["init_url"] = opts.InitUrl
	}
	err = cfg.DB().Model(data).Updates(optsMap).Error

	return data, err
}
