package service

import (
	"cold-delivery/app/admin/model"
	"cold-delivery/app/admin/service/dto"
	"cold-delivery/common/actions"
	cDto "cold-delivery/common/dto"
	"cold-delivery/common/global"
	"errors"
	"fmt"
	"strings"

	"github.com/casbin/casbin/v2"
	"gogs.baozhida.cn/zoie/OAuth-core/sdk"
	"gogs.baozhida.cn/zoie/OAuth-core/sdk/config"
	"gogs.baozhida.cn/zoie/OAuth-core/service"
	"gorm.io/gorm"
)

type SysPost struct {
	service.Service
}

// GetPage 分页查询岗位列表
func (e *SysPost) GetPage(c *dto.SysPostGetPageReq, list *[]model.SysPost, count *int64, p *actions.DataPermission) error {
	var err error
	var data model.SysPost

	db := e.Orm.Model(&data).
		Scopes(
			cDto.MakeCondition(c.GetNeedSearch()),
			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
		)
	// 超级管理员（deptId=0）可以看到所有部门的岗位
	if p.DeptId != 0 {
		db = db.Scopes(actions.Permission(data.TableName(), p))
	}

	err = db.Find(list).Limit(-1).Offset(-1).
		Count(count).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return global.GetFailedErr
	}
	return nil
}

// Get 获取单个岗位详情（含权限菜单ID列表）
func (e *SysPost) Get(d *dto.SysPostGetReq, postModel *model.SysPost, p *actions.DataPermission) error {
	db := e.Orm
	// 超级管理员可以看到所有部门的岗位
	if p.DeptId != 0 {
		db = db.Scopes(actions.Permission(postModel.TableName(), p))
	}
	err := db.First(postModel, d.GetId()).Error

	if err != nil {
		e.Log.Errorf("db error: %s", err)
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return global.GetNotFoundOrNoPermissionErr
		}
		return global.GetFailedErr
	}

	postModel.MenuIds, err = e.GetPostMenuIds(postModel.Id)
	if err != nil {
		e.Log.Errorf("get menuIds error, %s", err)
		return global.GetFailedErr
	}

	return nil
}

// Insert 创建岗位（含菜单权限分配）
func (e *SysPost) Insert(c *dto.SysPostInsertReq) error {
	var err error
	var data model.SysPost

	tx := e.Orm.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()

	// 检查岗位编码在同一个部门下是否已存在
	var count int64
	err = tx.Model(&data).Where("post_code = ? AND dept_id = ?", c.PostCode, c.DeptId).Count(&count).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return global.CreateFailedErr
	}
	if count > 0 {
		return errors.New("该部门下岗位编码已存在")
	}

	// 创建岗位
	c.Generate(&data)
	err = tx.Create(&data).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return global.CreateFailedErr
	}
	c.Id = data.Id

	// 添加岗位菜单关联
	if len(c.MenuIds) > 0 {
		var menuList []model.SysMenu
		err = tx.Where("id in (?)", c.MenuIds).Find(&menuList).Error
		if err != nil {
			return global.GetFailedErr
		}
		if len(menuList) == 0 {
			return errors.New("菜单不存在！")
		}

		var postMenuList []model.SysPostMenu
		for _, v := range menuList {
			postMenuObj := model.SysPostMenu{
				PostId: data.Id,
				MenuId: v.Id,
			}
			postMenuObj.SetCreateBy(c.CreateBy)
			postMenuList = append(postMenuList, postMenuObj)
		}
		err = tx.Save(&postMenuList).Error
		if err != nil {
			e.Log.Errorf("save menuIds error, %s", err)
			return global.UpdateFailedErr
		}

		// 同步写入 Casbin 规则
		err = e.syncCasbinRules(tx, data, menuList)
		if err == nil {
			cb := sdk.Runtime.GetCasbinKey(config.ApplicationConfig.Host)
			if cb != nil {
				cb.LoadPolicy()
			}
		}
		if err != nil {
			e.Log.Errorf("sync casbin rules error, %s", err)
			return global.UpdateFailedErr
		}
	}

	return nil
}

// Update 修改岗位（含菜单权限重新分配）
func (e *SysPost) Update(c *dto.SysPostUpdateReq, p *actions.DataPermission, cb *casbin.SyncedEnforcer) error {
	var err error

	tx := e.Orm.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()

	var postModel = model.SysPost{}
	// 查询岗位是否存在
	db := e.Orm
	if p.DeptId != 0 {
		db = db.Scopes(actions.Permission(postModel.TableName(), p))
	}
	err = db.First(&postModel, c.GetId()).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return global.UpdateNotFoundOrNoPermissionErr
		}
		return global.UpdateFailedErr
	}

	// 更新岗位基本信息
	c.Generate(&postModel)
	// deptId 由 Generate 方法从更新请求中设置，允许修改部门
	err = tx.Save(&postModel).Error
	if err != nil {
		if errors.Is(err, model.ErrForbidUpdateSysPost) {
			return err
		}
		e.Log.Errorf("db error: %s", err)
		return global.UpdateFailedErr
	}

	// 清除旧的 Casbin 规则（用旧的 postCode）
	_, _ = cb.RemoveFilteredPolicy(0, "role:"+postModel.PostCode)
	_, _ = cb.RemoveFilteredPolicy(0, postModel.PostCode) // 清除旧格式规则（无 role: 前缀）

	// 删除旧的菜单关联
	err = tx.Where("post_id = ?", postModel.Id).Delete(&model.SysPostMenu{}).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return global.UpdateFailedErr
	}

	// 重新添加菜单关联
	if len(c.MenuIds) > 0 {
		var menuList []model.SysMenu
		err = e.Orm.Where("id in (?)", c.MenuIds).Find(&menuList).Error
		if err != nil {
			return global.GetFailedErr
		}
		if len(menuList) == 0 {
			return errors.New("菜单不存在！")
		}

		var postMenuList []model.SysPostMenu
		for _, v := range menuList {
			postMenuObj := model.SysPostMenu{
				PostId: postModel.Id,
				MenuId: v.Id,
			}
			postMenuObj.SetCreateBy(c.UpdateBy)
			postMenuList = append(postMenuList, postMenuObj)
		}
		err = tx.Save(&postMenuList).Error
		if err != nil {
			e.Log.Errorf("save menuIds error, %s", err)
			return global.UpdateFailedErr
		}

		// 同步写入 Casbin 规则
		err = e.syncCasbinRules(tx, postModel, menuList)
		if err == nil {
			cb.LoadPolicy()
		}
		if err != nil {
			e.Log.Errorf("sync casbin rules error, %s", err)
			return global.UpdateFailedErr
		}
	}

	// 清除缓存
	_ = model.DeletePostMenuCache(postModel.Id)

	c.Id = postModel.Id
	return nil
}

// Remove 删除岗位（逻辑删除）
func (e *SysPost) Remove(c *dto.SysPostDeleteReq, p *actions.DataPermission, cb *casbin.SyncedEnforcer) error {
	var err error

	tx := e.Orm.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()

	// 检查是否有用户关联该岗位
	var userCount int64
	err = tx.Table("sys_user").Where("post_id = ? AND deleted_at IS NULL", c.Id).Count(&userCount).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return global.DeleteFailedErr
	}
	if userCount > 0 {
		return errors.New(fmt.Sprintf("有 %d 个用户关联了该岗位，禁止删除！", userCount))
	}

	var postModel model.SysPost
	rdb := e.Orm
	if p.DeptId != 0 {
		rdb = rdb.Scopes(actions.Permission(postModel.TableName(), p))
	}
	err = rdb.First(&postModel, c.GetId()).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return global.DeleteNotFoundOrNoPermissionErr
		}
		return global.DeleteFailedErr
	}

	// GORM 逻辑删除（自动设置 deleted_at）
	db := tx.Delete(&postModel)
	if err = db.Error; err != nil {
		if errors.Is(err, model.ErrForbidDeleteSysPost) {
			return err
		}
		e.Log.Errorf("db error: %s", err)
		return global.DeleteFailedErr
	}
	if db.RowsAffected == 0 {
		return global.DeleteNotFoundOrNoPermissionErr
	}

	// 删除岗位关联的菜单
	err = tx.Where("post_id = ?", postModel.Id).Delete(&model.SysPostMenu{}).Error
	if err != nil {
		return global.UpdateFailedErr
	}

	// 清除 Casbin 规则
	_, _ = cb.RemoveFilteredPolicy(0, "role:"+postModel.PostCode)
	_, _ = cb.RemoveFilteredPolicy(0, postModel.PostCode) // 清除旧格式规则（无 role: 前缀）

	// 清除缓存
	_ = model.DeletePostMenuCache(postModel.Id)

	return nil
}

// GetAll 获取所有岗位（下拉列表用）
func (e *SysPost) GetAll(deptId int, list *[]model.SysPost, p *actions.DataPermission) error {
	var err error
	var data model.SysPost
	db := e.Orm.Model(&data)
	// 超级管理员可以看到所有部门的岗位
	if p.DeptId != 0 {
		db = db.Scopes(actions.Permission(data.TableName(), p))
	}
	if deptId > 0 {
		db = db.Where("dept_id = ?", deptId)
	}
	err = db.Order("sort asc").Find(list).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return global.GetFailedErr
	}
	return nil
}

// GetPostMenuIds 获取岗位已分配的菜单ID列表
func (e *SysPost) GetPostMenuIds(postId int) ([]int, error) {
	var menuIds []int
	err := e.Orm.Model(&model.SysPostMenu{}).Where("post_id = ?", postId).Pluck("menu_id", &menuIds).Error
	if err != nil {
		return []int{}, global.GetFailedErr
	}
	return menuIds, nil
}

// GetMenuTree 获取完整菜单树（用于权限分配界面）
func (e *SysPost) GetMenuTree() ([]dto.MenuLabel, error) {
	// 直接查询所有菜单，不依赖 SysMenu.GetList 的复杂搜索逻辑
	var menuList []model.SysMenu
	err := e.Orm.Order("sort asc").Find(&menuList).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return nil, global.GetFailedErr
	}

	// 构建菜单树
	tree := make([]dto.MenuLabel, 0)
	for i := 0; i < len(menuList); i++ {
		if menuList[i].ParentId != 0 {
			continue
		}
		label := dto.MenuLabel{}
		label.Id = menuList[i].Id
		label.Label = menuList[i].Title
		buildMenuLabelTree(&menuList, &label)
		tree = append(tree, label)
	}
	return tree, nil
}

// buildMenuLabelTree 递归构建菜单标签树
func buildMenuLabelTree(menuList *[]model.SysMenu, parent *dto.MenuLabel) {
	list := *menuList
	children := make([]dto.MenuLabel, 0)
	for j := 0; j < len(list); j++ {
		if parent.Id != list[j].ParentId {
			continue
		}
		child := dto.MenuLabel{}
		child.Id = list[j].Id
		child.Label = list[j].Title
		child.Children = []dto.MenuLabel{}
		if list[j].MenuType != "F" {
			buildMenuLabelTree(menuList, &child)
		}
		children = append(children, child)
	}
	if len(children) > 0 {
		parent.Children = children
	} else {
		parent.Children = nil
	}
}

// FilterMenuTree 根据允许的菜单ID过滤菜单树
func (e *SysPost) FilterMenuTree(fullTree []dto.MenuLabel, allowedIds []int) []dto.MenuLabel {
	if len(allowedIds) == 0 {
		return nil
	}
	allowedMap := make(map[int]bool)
	for _, id := range allowedIds {
		allowedMap[id] = true
	}

	var filter func(nodes []dto.MenuLabel) []dto.MenuLabel
	filter = func(nodes []dto.MenuLabel) []dto.MenuLabel {
		result := make([]dto.MenuLabel, 0)
		for _, node := range nodes {
			// Only include if allowed or has allowed children
			if allowedMap[node.Id] || hasAllowedChild(node.Children, allowedMap) {
				child := dto.MenuLabel{
					Id:       node.Id,
					Label:    node.Label,
					Children: filter(node.Children),
				}
				result = append(result, child)
			}
		}
		return result
	}
	return filter(fullTree)
}

func hasAllowedChild(children []dto.MenuLabel, allowedMap map[int]bool) bool {
	for _, c := range children {
		if allowedMap[c.Id] || hasAllowedChild(c.Children, allowedMap) {
			return true
		}
	}
	return false
}

// syncCasbinRules 同步岗位权限到 Casbin 规则表
func (e *SysPost) syncCasbinRules(tx *gorm.DB, post model.SysPost, menuList []model.SysMenu) error {
	// 获取完整的菜单信息（包括父菜单的路由权限）
	var allMenus []model.SysMenu
	menuIds := make([]int, 0)
	for _, m := range menuList {
		menuIds = append(menuIds, m.Id)
	}

	// 递归获取所有父级菜单的权限路径
	err := e.resolveMenuPaths(menuIds, &allMenus)
	if err != nil {
		return err
	}

	// 为每个菜单路径写入 Casbin 规则
	// Casbin 规则格式: p = postCode, domain, path, method
	// 在 Casbin 中: sub = postCode, dom = service::api, obj = path, act = method
	for _, menu := range allMenus {
		if menu.Path == "" {
			continue
		}

		var methods []string
		if menu.MenuType == "C" {
			methods = []string{"GET"}
		} else if menu.MenuType == "M" {
			continue
		} else if menu.MenuType == "F" {
			methods = mapButtonToMethods(menu.Title)
			if len(methods) == 0 {
				continue
			}
		}
		for _, method := range methods {
			// 使用原始 SQL 插入避免与 Casbin adapter 冲突
			// 检查是否已存在
			var count int64
			sub := "role:" + post.PostCode
			tx.Table("sys_casbin_rule").
				Where("p_type = 'p' AND v0 = ? AND v1 = ? AND v2 = ? AND v3 = ?",
					sub, "service::api", menu.Path, method).
				Count(&count)
			if count == 0 {
				err = tx.Exec(
					"INSERT INTO sys_casbin_rule (p_type, v0, v1, v2, v3, v4, v5) VALUES ('p', ?, ?, ?, ?, '', '')",
					sub, "service::api", menu.Path, method,
				).Error
				if err != nil {
					return err
				}
			}
		}
	}

	// 写入 g 规则（Casbin RBAC 角色继承）
	sub := "role:" + post.PostCode
	var gCount int64
	tx.Table("sys_casbin_rule").
		Where("p_type = 'g' AND v0 = ? AND v1 = ? AND v2 = ?", sub, sub, "service::api").
		Count(&gCount)
	if gCount == 0 {
		err = tx.Exec(
			"INSERT INTO sys_casbin_rule (p_type, v0, v1, v2, v3, v4, v5) VALUES ('g', ?, ?, ?, '', '', '')",
			sub, sub, "service::api",
		).Error
		if err != nil {
			return err
		}
	}
	return nil
}

// mapButtonToMethods 将按钮名称映射为 HTTP 方法
func mapButtonToMethods(title string) []string {
	title = strings.TrimSpace(title)
	switch {
	case strings.Contains(title, "查询"):
		return []string{"GET"}
	case strings.Contains(title, "新增") || strings.Contains(title, "导入"):
		return []string{"POST"}
	case strings.Contains(title, "编辑") || strings.Contains(title, "修改"):
		return []string{"PUT"}
	case strings.Contains(title, "删除"):
		return []string{"DELETE"}
	case strings.Contains(title, "重置") || strings.Contains(title, "审核"):
		return []string{"PUT"}
	case strings.Contains(title, "分配"):
		return []string{"POST"}
	default:
		return []string{"GET"}
	}
}

// resolveMenuPaths 递归获取菜单及其所有父菜单
func (e *SysPost) resolveMenuPaths(menuIds []int, result *[]model.SysMenu) error {
	if len(menuIds) == 0 {
		return nil
	}

	var menus []model.SysMenu
	err := e.Orm.Where("id in (?)", menuIds).Find(&menus).Error
	if err != nil {
		return err
	}

	parentIds := make([]int, 0)
	seen := make(map[int]bool)
	for _, m := range *result {
		seen[m.Id] = true
	}

	for _, m := range menus {
		if !seen[m.Id] {
			*result = append(*result, m)
			seen[m.Id] = true
		}
		if m.ParentId != 0 && !seen[m.ParentId] {
			parentIds = append(parentIds, m.ParentId)
		}
	}

	if len(parentIds) > 0 {
		return e.resolveMenuPaths(parentIds, result)
	}

	return nil
}

// GetWithName 根据岗位名称查询
func (e *SysPost) GetWithName(name string, deptId int, postModel *model.SysPost) *SysPost {
	db := e.Orm.Where("post_name = ?", name)
	if deptId > 0 {
		db = db.Where("dept_id = ?", deptId)
	}
	err := db.First(postModel).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = global.GetNotFoundOrNoPermissionErr
		} else {
			err = global.GetFailedErr
		}
		_ = e.AddError(err)
		return e
	}
	return e
}

// GetPostNamesByIds 根据岗位ID列表获取岗位名称映射
func (e *SysPost) GetPostNamesByIds(ids []int) (map[int]string, error) {
	if len(ids) == 0 {
		return map[int]string{}, nil
	}
	var posts []model.SysPost
	err := e.Orm.Where("id in (?)", ids).Find(&posts).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		return nil, global.GetFailedErr
	}
	result := make(map[int]string)
	for _, p := range posts {
		result[p.Id] = p.PostName
	}
	return result, nil
}

// GetPostById 通过岗位ID获取菜单权限标识列表
func (e *SysPost) GetPostById(postId int) ([]string, error) {
	permissions := make([]string, 0)
	postModel := model.SysPost{}
	postModel.Id = postId
	err := e.Orm.Model(&postModel).First(&postModel).Error
	if err != nil {
		e.Log.Errorf("db error: %s", err)
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, global.GetNotFoundOrNoPermissionErr
		}
		return nil, global.GetFailedErr
	}

	// 查询岗位关联的菜单权限标识
	var menuPerms []string
	err = e.Orm.Model(&model.SysMenu{}).
		Joins("JOIN sys_post_menu ON sys_post_menu.menu_id = sys_menu.id").
		Where("sys_post_menu.post_id = ? AND sys_menu.permission != ''", postId).
		Pluck("sys_menu.permission", &menuPerms).Error
	if err != nil {
		return nil, global.GetFailedErr
	}
	permissions = append(permissions, menuPerms...)
	return permissions, nil
}
