package resref

import (
	"context"
	"errors"

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

const (
	tableName = "resource_relations"
)

type ResourceRelationDaoIface interface {
	CreateResourceRelation(ctx context.Context, tx *gorm.DB, ent *entity.ResourceRelation) error
	UpsertResourceRelation(ctx context.Context, tx *gorm.DB, ent *entity.ResourceRelation) error
	BatchUpsertResourceRelations(ctx context.Context, tx *gorm.DB, ents []*entity.ResourceRelation) error
	GetResourceRelation(ctx context.Context, tx *gorm.DB, resType string, resID uint64, parentType string) (*entity.ResourceRelation, error)
	GetResourceRelationsByResource(ctx context.Context, tx *gorm.DB, resType string, resID uint64) ([]entity.ResourceRelation, error)
	//GetResourceRelationsByParent(ctx context.Context, tx *gorm.DB, parentType string, parentID uint64, resType string) ([]entity.ResourceRelation, error)
	GetResourceIDsByParentAndResourceIDs(ctx context.Context, tx *gorm.DB, parentType string, parentID uint64, resType string, resIDs []uint64) ([]uint64, error)
	BatchListResourceRelations(ctx context.Context, tx *gorm.DB, start uint64, limit int) ([]entity.ResourceRelation, error)
	DeleteResourceRelationsByResource(ctx context.Context, tx *gorm.DB, resType string, resIDs []uint64) error
	DeleteResourceRelationsByParent(ctx context.Context, tx *gorm.DB, parentType string, parentID uint64) error
}

type ResourceRelationDao struct {
}

func NewResourceRelationDao() ResourceRelationDaoIface {
	return &ResourceRelationDao{}
}

func (dao *ResourceRelationDao) CreateResourceRelation(ctx context.Context, tx *gorm.DB, ent *entity.ResourceRelation) error {
	err := tx.Table(tableName).Save(ent).Error
	if err != nil {
		return errs.WrapGORMError(err, "CreateResourceRelation error")
	}
	return nil
}

func (dao *ResourceRelationDao) UpsertResourceRelation(ctx context.Context, tx *gorm.DB, ent *entity.ResourceRelation) error {
	err := tx.Table(tableName).Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "resource_type"}, {Name: "resource_id"}, {Name: "parent_type"}},
		DoUpdates: clause.AssignmentColumns([]string{"parent_id"}),
	}).Create(ent).Error
	if err != nil {
		return errs.WrapGORMError(err, "UpsertResourceRelation error")
	}
	return nil
}

func (dao *ResourceRelationDao) BatchUpsertResourceRelations(ctx context.Context, tx *gorm.DB, ents []*entity.ResourceRelation) error {
	if len(ents) == 0 {
		return nil
	}
	err := tx.Table(tableName).Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "resource_type"}, {Name: "resource_id"}, {Name: "parent_type"}},
		DoUpdates: clause.AssignmentColumns([]string{"parent_id"}),
	}).Create(ents).Error
	if err != nil {
		return errs.WrapGORMError(err, "BatchUpsertResourceRelations error")
	}
	return nil
}

func (dao *ResourceRelationDao) GetResourceRelation(ctx context.Context, tx *gorm.DB, resType string, resID uint64, parentType string) (*entity.ResourceRelation, error) {
	var res = &entity.ResourceRelation{}
	err := tx.Table(tableName).Where("resource_type=? and resource_id=? and parent_type = ?",
		resType, resID, parentType).First(res).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errs.WrapGORMError(err, "GetResourceRelation error")
	}
	return res, nil
}

func (dao *ResourceRelationDao) GetResourceRelationsByResource(ctx context.Context, tx *gorm.DB, resType string, resID uint64) ([]entity.ResourceRelation, error) {
	var res []entity.ResourceRelation
	err := tx.Table(tableName).Where("resource_type=? and resource_id=?", resType, resID).Find(&res).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errs.WrapGORMError(err, "GetResourceRelationsByResource error")
	}
	return res, nil
}

//func (dao *ResourceRelationDao) GetResourceRelationsByParent(ctx context.Context, tx *gorm.DB, parentType string, parentID uint64, resType string) ([]entity.ResourceRelation, error) {
//	var res []entity.ResourceRelation
//	where := tx.Table(tableName).Where("parent_type=? and parent_id=?", parentType, parentID)
//	if resType != "" {
//		where = where.Where("resource_type=?", resType)
//	}
//	err := where.Find(&res).Error
//	if err != nil {
//		if errors.Is(err, gorm.ErrRecordNotFound) {
//			return nil, nil
//		}
//		return nil, errs.WrapGORMError(err, "GetResourceRelationsByParent error")
//	}
//	return res, nil
//}

func (dao *ResourceRelationDao) GetResourceIDsByParentAndResourceIDs(ctx context.Context, tx *gorm.DB, parentType string, parentID uint64, resType string, resIDs []uint64) ([]uint64, error) {
	var res []uint64
	where := tx.Table(tableName).
		Select("resource_id").
		Where("parent_type=? and parent_id=?", parentType, parentID)
	if resType != "" {
		where = where.Where("resource_type=?", resType)
	}
	if len(resIDs) > 0 {
		where = where.Where("resource_id in ?", resIDs)
	}
	err := where.Find(&res).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, errs.WrapGORMError(err, "GetResourceIDsByParentAndResourceIDs error")
	}
	return res, nil
}

func (dao *ResourceRelationDao) BatchListResourceRelations(ctx context.Context, tx *gorm.DB, start uint64, limit int) ([]entity.ResourceRelation, error) {
	var res []entity.ResourceRelation
	err := tx.Table(tableName).Where("id > ?", start).Order("id asc").Limit(limit).Find(&res).Error
	return res, err
}

func (dao *ResourceRelationDao) DeleteResourceRelationsByResource(ctx context.Context, tx *gorm.DB, resType string, resIDs []uint64) error {
	err := tx.Table(tableName).Where("resource_type=? and resource_id in ?", resType, resIDs).Delete(&entity.ResourceRelation{}).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil
		}
		return errs.WrapGORMError(err, "DeleteResourceRelationsByResource error")
	}
	return nil
}

func (dao *ResourceRelationDao) DeleteResourceRelationsByParent(ctx context.Context, tx *gorm.DB, parentType string, parentID uint64) error {
	err := tx.Table(tableName).Where("parent_type = ? and parent_id = ?", parentType, parentID).Delete(&entity.ResourceRelation{}).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil
		}
		return errs.WrapGORMError(err, "DeleteResourceRelationsByParent error")
	}
	return nil
}
