package repository

import (
	"context"
	"time"

	libmongo "gateway/internal/library/mongo"
	permission "gateway/internal/model/permission/domain"
	"gateway/internal/model/permission/domain/entity"
	domrepo "gateway/internal/model/permission/domain/repository"

	"go.mongodb.org/mongo-driver/v2/bson"
	mongodriver "go.mongodb.org/mongo-driver/v2/mongo"
)

// RolePermissionRepositoryParam configures the Mongo role-permission repository.
type RolePermissionRepositoryParam struct {
	Conf *libmongo.Conf
}

type rolePermissionRepository struct {
	db libmongo.DocumentDBUseCase
}

// NewRolePermissionRepository creates a Mongo-backed RolePermissionRepository.
func NewRolePermissionRepository(param RolePermissionRepositoryParam) domrepo.RolePermissionRepository {
	documentDB, err := libmongo.NewDocumentDB(param.Conf, entity.RolePermission{}.CollectionName())
	if err != nil {
		panic(err)
	}
	return &rolePermissionRepository{db: documentDB}
}

func (r *rolePermissionRepository) Insert(ctx context.Context, rp *entity.RolePermission) error {
	now := time.Now().UTC().UnixMilli()
	if rp.ID.IsZero() {
		rp.ID = bson.NewObjectID()
	}
	if rp.CreateAt == 0 {
		rp.CreateAt = now
	}
	if rp.UpdateAt == 0 {
		rp.UpdateAt = now
	}
	_, err := r.db.GetClient().InsertOne(ctx, rp)
	if err != nil && mongodriver.IsDuplicateKeyError(err) {
		return nil
	}
	return err
}

func (r *rolePermissionRepository) BulkInsert(ctx context.Context, rps []*entity.RolePermission) error {
	if len(rps) == 0 {
		return nil
	}
	now := time.Now().UTC().UnixMilli()
	docs := make([]any, 0, len(rps))
	for _, rp := range rps {
		if rp.ID.IsZero() {
			rp.ID = bson.NewObjectID()
		}
		if rp.CreateAt == 0 {
			rp.CreateAt = now
		}
		if rp.UpdateAt == 0 {
			rp.UpdateAt = now
		}
		docs = append(docs, rp)
	}
	_, err := r.db.GetClient().InsertMany(ctx, docs)
	if err != nil && !mongodriver.IsDuplicateKeyError(err) {
		return err
	}
	return nil
}

func (r *rolePermissionRepository) DeleteByRole(ctx context.Context, tenantID, roleID string) error {
	filter := bson.M{
		permission.BSONFieldTenantID: tenantID,
		permission.BSONFieldRoleID:   roleID,
	}
	_, err := r.db.GetClient().DeleteMany(ctx, filter)
	return err
}

func (r *rolePermissionRepository) DeleteByPermission(ctx context.Context, permissionID string) (int64, error) {
	filter := bson.M{permission.BSONFieldPermissionID: permissionID}
	res, err := r.db.GetClient().DeleteMany(ctx, filter)
	return res, err
}

func (r *rolePermissionRepository) SetForRole(
	ctx context.Context,
	tenantID, roleID string,
	permissionIDs []string,
) error {
	if err := r.DeleteByRole(ctx, tenantID, roleID); err != nil {
		return err
	}
	if len(permissionIDs) == 0 {
		return nil
	}
	now := time.Now().UTC().UnixMilli()
	rows := make([]*entity.RolePermission, 0, len(permissionIDs))
	for _, pid := range permissionIDs {
		rows = append(rows, &entity.RolePermission{
			ID:           bson.NewObjectID(),
			TenantID:     tenantID,
			RoleID:       roleID,
			PermissionID: pid,
			CreateAt:     now,
			UpdateAt:     now,
		})
	}
	return r.BulkInsert(ctx, rows)
}

func (r *rolePermissionRepository) ListByRole(
	ctx context.Context,
	tenantID, roleID string,
) ([]*entity.RolePermission, error) {
	q := bson.M{
		permission.BSONFieldTenantID: tenantID,
		permission.BSONFieldRoleID:   roleID,
	}
	var docs []*entity.RolePermission
	if err := r.db.GetClient().Find(ctx, &docs, q); err != nil {
		return nil, err
	}
	return docs, nil
}

func (r *rolePermissionRepository) ListByRoles(
	ctx context.Context,
	tenantID string,
	roleIDs []string,
) ([]*entity.RolePermission, error) {
	if len(roleIDs) == 0 {
		return nil, nil
	}
	q := bson.M{
		permission.BSONFieldTenantID: tenantID,
		permission.BSONFieldRoleID:   bson.M{bsonOpIn: roleIDs},
	}
	var docs []*entity.RolePermission
	if err := r.db.GetClient().Find(ctx, &docs, q); err != nil {
		return nil, err
	}
	return docs, nil
}

func (r *rolePermissionRepository) ListByTenant(
	ctx context.Context,
	tenantID string,
) ([]*entity.RolePermission, error) {
	q := bson.M{permission.BSONFieldTenantID: tenantID}
	var docs []*entity.RolePermission
	if err := r.db.GetClient().Find(ctx, &docs, q); err != nil {
		return nil, err
	}
	return docs, nil
}

// Index20260521001UP ensures role_permissions collection indexes exist.
func (r *rolePermissionRepository) Index20260521001UP(ctx context.Context) error {
	if err := r.db.PopulateMultiIndex(ctx,
		[]string{permission.BSONFieldTenantID, permission.BSONFieldRoleID, permission.BSONFieldPermissionID},
		[]int32{1, 1, 1}, true); err != nil {
		return err
	}
	return r.db.PopulateMultiIndex(ctx,
		[]string{permission.BSONFieldTenantID, permission.BSONFieldPermissionID},
		[]int32{1, 1}, false)
}

var _ domrepo.RolePermissionRepository = (*rolePermissionRepository)(nil)
