package scim

import (
	"context"
	"errors"
	"fmt"

	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/tiinf/errs"
	"gorm.io/gorm"
)

const (
	tableName = "scim_groups"
)

type ScimGroupDaoIface interface {
	CreateScimGroup(ctx context.Context, tx *gorm.DB, group *entity.ScimGroup) (*entity.ScimGroup, error)
	UpdateScimGroup(ctx context.Context, tx *gorm.DB, group *entity.ScimGroup) error
	DeleteScimGroupByID(ctx context.Context, tx *gorm.DB, id uint64) error
	GetScimGroupByGroupID(ctx context.Context, tx *gorm.DB, groupID string) (*entity.ScimGroup, error)
	ListScimGroupsByOrgID(ctx context.Context, tx *gorm.DB, orgID uint64) ([]entity.ScimGroup, error)
	BatchGetScimGroupsByOrgIDSelectiveDisplayName(ctx context.Context, tx *gorm.DB, orgID uint64, displayName string, offset, limit int) ([]entity.ScimGroup, error)
	CountScimGroupsByOrgIDSelectiveDisplayName(ctx context.Context, tx *gorm.DB, orgID uint64, displayName string) (int, error)
}

type ScimGroupDao struct {
}

func NewScimGroupDao() ScimGroupDaoIface {
	return &ScimGroupDao{}
}

func (dao *ScimGroupDao) CreateScimGroup(ctx context.Context, tx *gorm.DB, group *entity.ScimGroup) (*entity.ScimGroup, error) {
	err := tx.Table(tableName).Save(group).Error
	if err != nil {
		return nil, errs.WrapGORMError(err, fmt.Sprintf("create ScimGroup failed: %+v", group))
	}
	return group, nil
}

func (dao *ScimGroupDao) UpdateScimGroup(ctx context.Context, tx *gorm.DB, group *entity.ScimGroup) error {
	updateMap := map[string]interface{}{
		"display_name": group.DisplayName,
		"members":      group.Members,
		"org_role":     group.OrgRole,
		"project_role": group.ProjectRole,
	}

	err := tx.Table(tableName).Where("id=?", group.ID.Uint64()).Updates(updateMap).Error
	if err != nil {
		return errs.WrapGORMError(err, fmt.Sprintf("update ScimGroup failed: %+v", group))
	}
	return nil
}

func (dao *ScimGroupDao) DeleteScimGroupByID(ctx context.Context, tx *gorm.DB, id uint64) error {
	err := tx.Table(tableName).Where("id=?", id).Delete(&entity.ScimGroup{}).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil
	}
	return errs.WrapGORMError(err, fmt.Sprintf("delete db ScimGroup failed: %+v", id))
}

func (dao *ScimGroupDao) GetScimGroupByGroupID(ctx context.Context, tx *gorm.DB, groupID string) (*entity.ScimGroup, error) {
	res := &entity.ScimGroup{}
	err := tx.Table(tableName).Where("group_id=?", groupID).First(res).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errs.WrapGORMError(err, fmt.Sprintf("get db ScimGroup failed: %+v", groupID))
	}
	return res, nil
}

func (dao *ScimGroupDao) ListScimGroupsByOrgID(ctx context.Context, tx *gorm.DB, orgID uint64) ([]entity.ScimGroup, error) {
	var res []entity.ScimGroup
	err := tx.Table(tableName).Where("org_id=?", orgID).Order("created_at desc, id desc").Find(&res).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errs.WrapGORMError(err, fmt.Sprintf("list db ScimGroups failed: %+v", orgID))
	}
	return res, nil
}

func (dao *ScimGroupDao) BatchGetScimGroupsByOrgIDSelectiveDisplayName(ctx context.Context, tx *gorm.DB, orgID uint64, displayName string, offset, limit int) ([]entity.ScimGroup, error) {
	var res []entity.ScimGroup
	db := tx.Table(tableName).Where("org_id=?", orgID)
	if displayName != "" {
		db = db.Where("display_name=?", displayName)
	}
	err := db.Limit(limit).Offset(offset).Order("created_at desc, id desc").Find(&res).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errs.WrapGORMError(err, fmt.Sprintf("list db ScimGroups failed: %+v", orgID))
	}
	return res, nil
}

func (dao *ScimGroupDao) CountScimGroupsByOrgIDSelectiveDisplayName(ctx context.Context, tx *gorm.DB, orgID uint64, displayName string) (int, error) {
	var total int64
	db := tx.Table(tableName).Where("org_id=?", orgID)
	if displayName != "" {
		db = db.Where("display_name=?", displayName)
	}
	err := db.Count(&total).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, nil
		}
		return 0, errs.WrapGORMError(err, fmt.Sprintf("count db ScimGroups failed: %+v", orgID))
	}
	res := int(total)
	return res, nil
}
