package adminrole

import (
	"errors"

	"hfb_sys/backend/internal/model"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type Repository struct {
	db *gorm.DB
}

func NewRepository(db *gorm.DB) *Repository {
	return &Repository{db: db}
}

func (r *Repository) List() ([]RoleDTO, error) {
	var roles []model.Role
	if err := r.db.Order("id ASC").Find(&roles).Error; err != nil {
		return nil, err
	}
	result := make([]RoleDTO, 0, len(roles))
	for _, role := range roles {
		var count int64
		r.db.Model(&model.RolePermission{}).Where("role_id = ?", role.ID).Count(&count)
		result = append(result, RoleDTO{
			ID:          role.ID,
			Code:        role.Code,
			Name:        role.Name,
			Description: role.Description,
			PermCount:   int(count),
			CreatedAt:   role.CreatedAt,
			UpdatedAt:   role.UpdatedAt,
		})
	}
	return result, nil
}

func (r *Repository) FindByID(id uint64) (*RoleDTO, error) {
	var role model.Role
	if err := r.db.First(&role, id).Error; err != nil {
		return nil, err
	}
	perms, err := r.getRolePermissions(id)
	if err != nil {
		return nil, err
	}
	dto := RoleDTO{
		ID:          role.ID,
		Code:        role.Code,
		Name:        role.Name,
		Description: role.Description,
		PermCount:   len(perms),
		Permissions: perms,
		CreatedAt:   role.CreatedAt,
		UpdatedAt:   role.UpdatedAt,
	}
	return &dto, nil
}

func (r *Repository) Create(req CreateRoleRequest) (*RoleDTO, error) {
	role := model.Role{
		Code:        req.Code,
		Name:        req.Name,
		Description: req.Description,
	}
	if err := r.db.Create(&role).Error; err != nil {
		return nil, err
	}
	return r.FindByID(role.ID)
}

func (r *Repository) Update(id uint64, req UpdateRoleRequest) (*RoleDTO, error) {
	var role model.Role
	if err := r.db.First(&role, id).Error; err != nil {
		return nil, err
	}
	role.Name = req.Name
	role.Description = req.Description
	if err := r.db.Save(&role).Error; err != nil {
		return nil, err
	}
	return r.FindByID(id)
}

func (r *Repository) Delete(id uint64) error {
	var role model.Role
	if err := r.db.First(&role, id).Error; err != nil {
		return err
	}
	if role.Code == "super_admin" {
		return ErrProtectedRole
	}
	return r.db.Transaction(func(tx *gorm.DB) error {
		if err := tx.Where("role_id = ?", id).Delete(&model.RolePermission{}).Error; err != nil {
			return err
		}
		if err := tx.Where("role_id = ?", id).Delete(&model.AdminUserRole{}).Error; err != nil {
			return err
		}
		return tx.Delete(&model.Role{}, id).Error
	})
}

func (r *Repository) AssignPermissions(roleID uint64, permIDs []uint64) error {
	var role model.Role
	if err := r.db.First(&role, roleID).Error; err != nil {
		return err
	}
	return r.db.Transaction(func(tx *gorm.DB) error {
		if err := tx.Where("role_id = ?", roleID).Delete(&model.RolePermission{}).Error; err != nil {
			return err
		}
		for _, pid := range permIDs {
			rp := model.RolePermission{RoleID: roleID, PermissionID: pid}
			if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&rp).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

func (r *Repository) ListPermissions() ([]PermissionDTO, error) {
	var perms []model.Permission
	if err := r.db.Order("resource ASC, action ASC").Find(&perms).Error; err != nil {
		return nil, err
	}
	result := make([]PermissionDTO, 0, len(perms))
	for _, p := range perms {
		result = append(result, PermissionDTO{
			ID: p.ID, Code: p.Code, Name: p.Name, Resource: p.Resource, Action: p.Action,
		})
	}
	return result, nil
}

func (r *Repository) getRolePermissions(roleID uint64) ([]PermissionDTO, error) {
	var rps []model.RolePermission
	if err := r.db.Where("role_id = ?", roleID).Find(&rps).Error; err != nil {
		return nil, err
	}
	if len(rps) == 0 {
		return nil, nil
	}
	ids := make([]uint64, 0, len(rps))
	for _, rp := range rps {
		ids = append(ids, rp.PermissionID)
	}
	var perms []model.Permission
	if err := r.db.Where("id IN ?", ids).Find(&perms).Error; err != nil {
		return nil, err
	}
	result := make([]PermissionDTO, 0, len(perms))
	for _, p := range perms {
		result = append(result, PermissionDTO{
			ID: p.ID, Code: p.Code, Name: p.Name, Resource: p.Resource, Action: p.Action,
		})
	}
	return result, nil
}

var ErrProtectedRole = errors.New("protected role")

func IsNotFound(err error) bool {
	return errors.Is(err, gorm.ErrRecordNotFound)
}
