package system

import (
	"context"
	"errors"
	"strconv"
	"strings"
	"time"

	"kra/internal/biz"

	"gorm.io/gorm"
)

type departmentRepo struct{ data Provider }

func NewDepartmentRepo(data Provider) biz.DepartmentRepo { return &departmentRepo{data: data} }

type departmentPO struct {
	ID        uint `gorm:"primaryKey"`
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt gorm.DeletedAt `gorm:"index"`
	Name      string         `gorm:"index"`
	ParentID  uint
	Ancestors string
	Sort      int
	LeaderID  uint
	Status    *bool `gorm:"default:true"`
}

func (departmentPO) TableName() string { return "sys_departments" }

type userDepartmentPO struct {
	// Match the reference join model: no composite primary key, so repeated IDs in a
	// request are persisted in the same order/shape as the reference service.
	UserID       uint `gorm:"column:sys_user_id"`
	DepartmentID uint `gorm:"column:sys_department_id"`
}

func (userDepartmentPO) TableName() string { return "sys_user_departments" }

type authorityDepartmentPO struct {
	AuthorityID  uint `gorm:"column:sys_authority_authority_id;index"`
	DepartmentID uint `gorm:"column:sys_department_id"`
}

func (authorityDepartmentPO) TableName() string { return "sys_authority_departments" }

func deptFromPO(po departmentPO) *biz.Department {
	return &biz.Department{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, ParentID: po.ParentID, Ancestors: po.Ancestors, Sort: po.Sort, LeaderID: po.LeaderID, Status: po.Status}
}

func (r *departmentRepo) attachDepartmentLeaders(ctx context.Context, departments []*biz.Department) error {
	leaderIDs := make([]uint, 0, len(departments))
	for _, department := range departments {
		if department.LeaderID != 0 {
			leaderIDs = append(leaderIDs, department.LeaderID)
		}
	}
	if len(leaderIDs) == 0 {
		return nil
	}
	var leaders []userPO
	if err := r.data.DB().WithContext(ctx).Where("id IN ?", leaderIDs).Find(&leaders).Error; err != nil {
		return err
	}
	leaderByID := make(map[uint]*biz.User, len(leaders))
	for i := range leaders {
		leaderByID[leaders[i].ID] = baseBizUser(&leaders[i])
	}
	for _, department := range departments {
		department.Leader = leaderByID[department.LeaderID]
	}
	return nil
}
func (r *departmentRepo) CreateDepartment(ctx context.Context, v *biz.Department) error {
	v.Ancestors = "0"
	if v.ParentID != 0 {
		var parent departmentPO
		if err := r.data.DB().WithContext(ctx).First(&parent, v.ParentID).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return errors.New("父部门不存在")
			}
			return err
		}
		v.Ancestors = strings.Trim(parent.Ancestors+","+strconv.FormatUint(uint64(parent.ID), 10), ",")
	}
	return r.data.DB().WithContext(ctx).Create(&departmentPO{Name: v.Name, ParentID: v.ParentID, Ancestors: v.Ancestors, Sort: v.Sort, LeaderID: v.LeaderID, Status: v.Status}).Error
}
func (r *departmentRepo) UpdateDepartment(ctx context.Context, v *biz.Department) error {
	if v.ParentID == v.ID {
		return errors.New("父部门不能是自己")
	}
	v.Ancestors = "0"
	if v.ParentID != 0 {
		var parent departmentPO
		if err := r.data.DB().WithContext(ctx).First(&parent, v.ParentID).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return errors.New("父部门不存在")
			}
			return err
		}
		v.Ancestors = strings.Trim(parent.Ancestors+","+strconv.FormatUint(uint64(parent.ID), 10), ",")
	}
	return r.data.DB().WithContext(ctx).Model(&departmentPO{}).Where("id = ?", v.ID).Updates(map[string]any{"name": v.Name, "parent_id": v.ParentID, "ancestors": v.Ancestors, "sort": v.Sort, "leader_id": v.LeaderID, "status": v.Status}).Error
}
func (r *departmentRepo) DeleteDepartment(ctx context.Context, id uint) error {
	if id == 0 {
		return errors.New("部门ID不能为空")
	}
	var count int64
	if err := r.data.DB().WithContext(ctx).Model(&departmentPO{}).Where("parent_id = ?", id).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("存在子部门,不允许删除")
	}
	if err := r.data.DB().WithContext(ctx).Model(&userPO{}).Where("dept_id = ?", id).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("该部门下存在用户,不允许删除")
	}
	if err := r.data.DB().WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("该部门下存在用户,不允许删除")
	}
	return r.data.DB().WithContext(ctx).Delete(&departmentPO{}, id).Error
}
func (r *departmentRepo) FindDepartment(ctx context.Context, id uint) (*biz.Department, error) {
	var po departmentPO
	if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
		return nil, err
	}
	department := deptFromPO(po)
	if err := r.attachDepartmentLeaders(ctx, []*biz.Department{department}); err != nil {
		return nil, err
	}
	return department, nil
}
func (r *departmentRepo) ListDepartments(ctx context.Context, name string) ([]*biz.Department, error) {
	var pos []departmentPO
	db := r.data.DB().WithContext(ctx).Order("sort")
	if name != "" {
		db = db.Where("name LIKE ?", "%"+name+"%")
	}
	if err := db.Find(&pos).Error; err != nil {
		return nil, err
	}
	nodes := map[uint]*biz.Department{}
	items := make([]*biz.Department, 0, len(pos))
	for _, po := range pos {
		item := deptFromPO(po)
		if name == "" {
			// Recursive GORM child queries initialize leaf collections to an empty
			// slice. Preserve that tree response shape while search and single-item
			// queries keep the model's nil children value.
			item.Children = []*biz.Department{}
		}
		nodes[po.ID] = item
		items = append(items, item)
	}
	if err := r.attachDepartmentLeaders(ctx, items); err != nil {
		return nil, err
	}
	if name != "" {
		return items, nil
	}
	roots := make([]*biz.Department, 0)
	for _, po := range pos {
		n := nodes[po.ID]
		if p := nodes[po.ParentID]; p != nil {
			p.Children = append(p.Children, n)
		} else if po.ParentID == 0 {
			roots = append(roots, n)
		}
	}
	return roots, nil
}
func (r *departmentRepo) DepartmentUserIDs(ctx context.Context, id uint) ([]uint, error) {
	var ids []uint
	err := r.data.DB().WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Pluck("sys_user_id", &ids).Error
	return ids, err
}
func (r *departmentRepo) SetDepartmentUsers(ctx context.Context, id uint, ids []uint) error {
	return r.replaceUserDepartments(ctx, id, ids)
}
func (r *departmentRepo) replaceUserDepartments(ctx context.Context, id uint, ids []uint) error {
	if id == 0 {
		return errors.New("部门ID不能为空")
	}
	return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var current []uint
		if err := tx.Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Pluck("sys_user_id", &current).Error; err != nil {
			return err
		}
		if err := tx.Where("sys_department_id = ?", id).Delete(&userDepartmentPO{}).Error; err != nil {
			return err
		}
		links := make([]userDepartmentPO, 0, len(ids))
		for _, uid := range ids {
			links = append(links, userDepartmentPO{UserID: uid, DepartmentID: id})
		}
		if len(links) > 0 {
			if err := tx.Create(&links).Error; err != nil {
				return err
			}
		}
		target := make(map[uint]struct{}, len(ids))
		for _, uid := range ids {
			target[uid] = struct{}{}
		}
		removed := make([]uint, 0)
		for _, uid := range current {
			if _, ok := target[uid]; !ok {
				removed = append(removed, uid)
			}
		}
		if len(removed) > 0 {
			if err := tx.Model(&userPO{}).Where("dept_id = ? AND id IN ?", id, removed).Update("dept_id", 0).Error; err != nil {
				return err
			}
		}
		if len(ids) > 0 {
			if err := tx.Model(&userPO{}).Where("dept_id = ? AND id IN ?", 0, ids).Update("dept_id", id).Error; err != nil {
				return err
			}
		}
		return nil
	})
}
func (r *departmentRepo) SetUserDepartments(ctx context.Context, uid uint, ids []uint, primary uint) error {
	return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var user userPO
		if err := tx.First(&user, uid).Error; err != nil {
			return errors.New("查询用户数据失败")
		}
		if err := tx.Where("sys_user_id = ?", uid).Delete(&userDepartmentPO{}).Error; err != nil {
			return err
		}
		links := make([]userDepartmentPO, 0, len(ids))
		for _, id := range ids {
			links = append(links, userDepartmentPO{UserID: uid, DepartmentID: id})
		}
		if len(links) > 0 {
			if err := tx.Create(&links).Error; err != nil {
				return err
			}
		}
		if primary == 0 && len(ids) > 0 {
			primary = ids[0]
		}
		if primary != 0 {
			inSet := false
			for _, id := range ids {
				if id == primary {
					inSet = true
					break
				}
			}
			if !inSet {
				return errors.New("主部门必须在归属部门范围内")
			}
		}
		return tx.Model(&userPO{}).Where("id = ?", uid).Update("dept_id", primary).Error
	})
}
