package user

import (
	"bsm/full/module/base/mgt/internal/impl"
	"bsm/full/module/base/mgt/internal/libs"
	mgtmw "bsm/full/module/base/mgt/internal/middleware"
	"bsm/full/module/base/mgt/internal/models"
	"bsm/full/module/base/mgt/internal/types"

	"git.apinb.com/bsm-sdk/core/errcode"
	"git.apinb.com/bsm-sdk/core/infra"
	"git.apinb.com/bsm-sdk/core/middleware"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

const permissionPreloadSelect = "id,identity,title,title_en,sort_key,parent_id,menu_path,menu_icon,code,component,is_web_page,is_new_tab,is_full,hide_menu,web_url,app_id"

func permissionOrder(db *gorm.DB) *gorm.DB { return db.Order("sort_key asc") }

func pmnPreloadWithWorkspace(workspace string) func(*gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		db = db.Select(permissionPreloadSelect)
		if workspace != "" {
			db = db.Where("app_id = (SELECT id FROM mgt_application WHERE workspace = ?)", workspace)
		}
		return permissionOrder(db)
	}
}

func mergeEffectivePermissions(u *models.MgtUser) []models.MgtPermission {
	seen := make(map[uint]struct{})
	out := make([]models.MgtPermission, 0)

	add := func(list []models.MgtPermission) {
		for _, p := range list {
			if p.ID == 0 {
				continue
			}
			if _, ok := seen[p.ID]; ok {
				continue
			}
			seen[p.ID] = struct{}{}
			out = append(out, p)
		}
	}

	add(u.Permissions)
	for i := range u.Roles {
		add(u.Roles[i].Permissions)
	}
	for i := range u.Departments {
		add(u.Departments[i].Permissions)
		for j := range u.Departments[i].Roles {
			add(u.Departments[i].Roles[j].Permissions)
		}
	}
	return out
}

func PermissionsFetch(c *gin.Context) {
	var (
		request = types.FetchBase{}
		data    = make([]models.MgtUser, 0)
		db      = impl.DBService.Model(&models.MgtUser{})
	)
	if err := c.ShouldBindJSON(&request); err != nil {
		infra.Response.Error(c, errcode.ErrJsonUnmarshal)
		return
	}
	if err := libs.ValidateStruct(&request); err != nil {
		infra.Response.Error(c, errcode.ErrInvalidArgument)
		return
	}
	auth, err := middleware.ParseAuth(c)
	if err != nil {
		infra.Response.Error(c, errcode.ErrTokenAuthParseFail)
		return
	}
	if request.ID == 0 {
		request.ID = auth.ID
	}
	if !mgtmw.EnsureSelfOrAdmin(c, request.ID) {
		return
	}

	ws := request.Workspace
	db = db.Where("id = ?", request.ID).Select("id", "identity", "name").
		Preload("Permissions", pmnPreloadWithWorkspace(ws)).
		Preload("Roles", func(db *gorm.DB) *gorm.DB {
			return db.Select("id", "identity", "name").Preload("Permissions", pmnPreloadWithWorkspace(ws))
		}).
		Preload("Departments", func(db *gorm.DB) *gorm.DB {
			return db.Select("id", "identity", "name").
				Preload("Permissions", pmnPreloadWithWorkspace(ws)).
				Preload("Roles", func(db *gorm.DB) *gorm.DB {
					return db.Select("id", "identity", "name").Preload("Permissions", pmnPreloadWithWorkspace(ws))
				})
		})

	if err := db.Find(&data).Error; err != nil {
		infra.Response.Error(c, errcode.ErrDB)
		return
	}

	if len(data) == 0 {
		infra.Response.Success(c, data)
		return
	}
	merged := mergeEffectivePermissions(&data[0])
	data[0].Permissions = merged
	infra.Response.Success(c, data)
}

func PermissionsFetchTree(c *gin.Context) {
	var (
		request = types.FetchBase{}
		data    = make([]models.MgtUser, 0)
	)
	if err := c.ShouldBindJSON(&request); err != nil {
		infra.Response.Error(c, errcode.ErrJsonUnmarshal)
		return
	}
	if err := libs.ValidateStruct(&request); err != nil {
		infra.Response.Error(c, errcode.ErrInvalidArgument)
		return
	}
	auth, err := middleware.ParseAuth(c)
	if err != nil {
		infra.Response.Error(c, errcode.ErrTokenAuthParseFail)
		return
	}
	if request.ID == 0 {
		request.ID = auth.ID
	}
	if !mgtmw.EnsureSelfOrAdmin(c, request.ID) {
		return
	}

	db := impl.DBService.Model(&models.MgtUser{}).Where("id = ?", request.ID).
		Select("id", "identity", "name").
		Preload("Permissions", func(db *gorm.DB) *gorm.DB {
			return db.Select(permissionPreloadSelect).Order("sort_key asc")
		}).
		Preload("Roles", func(db *gorm.DB) *gorm.DB {
			return db.Select("id", "identity", "name").Preload("Permissions", func(db *gorm.DB) *gorm.DB {
				return db.Select(permissionPreloadSelect).Order("sort_key asc")
			})
		}).
		Preload("Departments", func(db *gorm.DB) *gorm.DB {
			return db.Select("id", "identity", "name").
				Preload("Permissions", func(db *gorm.DB) *gorm.DB {
					return db.Select(permissionPreloadSelect).Order("sort_key asc")
				}).
				Preload("Roles", func(db *gorm.DB) *gorm.DB {
					return db.Select("id", "identity", "name").Preload("Permissions", func(db *gorm.DB) *gorm.DB {
						return db.Select(permissionPreloadSelect).Order("sort_key asc")
					})
				})
		})
	if err := db.Find(&data).Error; err != nil {
		infra.Response.Error(c, errcode.ErrDB)
		return
	}
	if len(data) == 0 {
		infra.Response.Success(c, data)
		return
	}

	merged := mergeEffectivePermissions(&data[0])
	pmnIdsByApp := make(map[uint][]uint)
	for _, p := range merged {
		if p.Appid == 0 {
			continue
		}
		pmnIdsByApp[p.Appid] = append(pmnIdsByApp[p.Appid], p.ID)
	}

	appDb := impl.DBService.Model(&models.MgtUser{}).Where("id = ?", request.ID).
		Select("id", "identity", "name")
	appDb = appDb.Preload("Application", func(db *gorm.DB) *gorm.DB {
		db = db.Select("id", "identity", "title", "workspace")
		if request.Workspace != "" {
			db = db.Where("workspace = ?", request.Workspace)
		}
		return db
	})
	var userWithApp []models.MgtUser
	if err := appDb.Find(&userWithApp).Error; err != nil || len(userWithApp) == 0 {
		infra.Response.Success(c, data)
		return
	}

	apps := userWithApp[0].Application
	if len(apps) == 0 {
		data[0].Application = nil
		infra.Response.Success(c, data)
		return
	}

	treeSelect := "id,identity,title,title_en,sort_key,parent_id,menu_path,menu_icon,code,component,is_web_page,is_new_tab,web_url,app_id"
	for i := range apps {
		ids := pmnIdsByApp[apps[i].ID]
		if len(ids) == 0 {
			apps[i].UsersPmn = nil
			apps[i].RolesPmn = nil
			continue
		}
		var tree []models.MgtPermission
		err := impl.DBService.Model(&models.MgtPermission{}).
			Select(treeSelect).
			Where("app_id = ? AND parent_id = 0 AND id IN ?", apps[i].ID, ids).
			Order("sort_key asc").
			Preload("Children", func(db *gorm.DB) *gorm.DB {
				return db.Select(treeSelect).Where("id IN ?", ids).Order("sort_key asc")
			}).
			Find(&tree).Error
		if err != nil {
			apps[i].UsersPmn = nil
			apps[i].RolesPmn = nil
			continue
		}
		apps[i].UsersPmn = tree
	}

	data[0].Application = apps
	infra.Response.Success(c, data)
}
