package repository

import (
	"backend/pkg/library/mongo"
	"backend/pkg/permission/domain"
	"backend/pkg/permission/domain/entity"
	"backend/pkg/permission/domain/repository"
	"context"
	"errors"
	"time"

	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/mon"
	"go.mongodb.org/mongo-driver/v2/bson"
	mongodriver "go.mongodb.org/mongo-driver/v2/mongo"
)

type RolePermissionRepositoryParam struct {
	Conf      *mongo.Conf
	CacheConf cache.CacheConf
	DBOpts    []mon.Option
	CacheOpts []cache.Option
}

type RolePermissionRepository struct {
	DB mongo.DocumentDBWithCacheUseCase
}

func NewRolePermissionRepository(param RolePermissionRepositoryParam) repository.RolePermissionRepository {
	e := entity.RolePermission{}
	documentDB, err := mongo.MustDocumentDBWithCache(
		param.Conf,
		e.CollectionName(),
		param.CacheConf,
		param.DBOpts,
		param.CacheOpts,
	)
	if err != nil {
		panic(err)
	}

	return &RolePermissionRepository{
		DB: documentDB,
	}
}

// Create 建立角色權限關聯
func (repo *RolePermissionRepository) Create(ctx context.Context, roleID int64, permissionIDs []int64) error {
	if len(permissionIDs) == 0 {
		return nil
	}

	now := time.Now().Unix()

	// 將 int64 轉換為 ObjectID
	roleOID := bson.NewObjectIDFromTimestamp(time.Unix(roleID, 0))

	// 批量建立角色權限關聯
	documents := make([]interface{}, 0, len(permissionIDs))
	for _, permissionID := range permissionIDs {
		permOID := bson.NewObjectIDFromTimestamp(time.Unix(permissionID, 0))
		rp := &entity.RolePermission{
			ID:           bson.NewObjectID(),
			RoleID:       roleOID,
			PermissionID: permOID,
		}
		rp.CreateTime = now
		rp.UpdateTime = now

		documents = append(documents, rp)
	}

	_, err := repo.DB.GetClient().InsertMany(ctx, documents)
	if err != nil {
		return err
	}

	// 清除快取
	repo.clearRolePermissionCache(ctx, roleID)

	return nil
}

// Update 更新角色權限關聯 (先刪除再建立)
func (repo *RolePermissionRepository) Update(ctx context.Context, roleID int64, permissionIDs []int64) error {
	// 先刪除該角色的所有權限關聯
	err := repo.Delete(ctx, roleID)
	if err != nil {
		return err
	}

	// 再建立新的關聯
	return repo.Create(ctx, roleID, permissionIDs)
}

// Delete 刪除角色的所有權限
func (repo *RolePermissionRepository) Delete(ctx context.Context, roleID int64) error {
	// 將 int64 轉換為 ObjectID
	roleOID := bson.NewObjectIDFromTimestamp(time.Unix(roleID, 0))

	filter := bson.M{"role_id": roleOID}

	_, err := repo.DB.GetClient().DeleteMany(ctx, filter)
	if err != nil {
		return err
	}

	// 清除快取
	repo.clearRolePermissionCache(ctx, roleID)

	return nil
}

// GetByRoleID 取得角色的所有權限關聯
func (repo *RolePermissionRepository) GetByRoleID(ctx context.Context, roleID int64) ([]*entity.RolePermission, error) {
	var data []*entity.RolePermission
	// 將 int64 轉換為 ObjectID
	roleOID := bson.NewObjectIDFromTimestamp(time.Unix(roleID, 0))

	filter := bson.M{"role_id": roleOID}

	err := repo.DB.GetClient().Find(ctx, &data, filter)
	if err != nil {
		if errors.Is(err, mon.ErrNotFound) {
			return []*entity.RolePermission{}, nil
		}
		return nil, err
	}

	return data, nil
}

// GetByRoleIDs 批量取得多個角色的權限關聯 (優化 N+1 查詢)
func (repo *RolePermissionRepository) GetByRoleIDs(ctx context.Context, roleIDs []int64) (map[int64][]*entity.RolePermission, error) {
	if len(roleIDs) == 0 {
		return make(map[int64][]*entity.RolePermission), nil
	}

	var data []*entity.RolePermission

	// 將 int64 轉換為 ObjectID
	roleOIDs := make([]bson.ObjectID, 0, len(roleIDs))
	oidToInt64 := make(map[string]int64) // 用於反向映射
	for _, roleID := range roleIDs {
		roleOID := bson.NewObjectIDFromTimestamp(time.Unix(roleID, 0))
		roleOIDs = append(roleOIDs, roleOID)
		oidToInt64[roleOID.Hex()] = roleID
	}

	filter := bson.M{
		"role_id": bson.M{"$in": roleOIDs},
	}

	err := repo.DB.GetClient().Find(ctx, &data, filter)
	if err != nil {
		if errors.Is(err, mon.ErrNotFound) {
			return make(map[int64][]*entity.RolePermission), nil
		}
		return nil, err
	}

	// 將結果按 roleID (int64) 分組
	result := make(map[int64][]*entity.RolePermission)
	for _, rp := range data {
		// 將 ObjectID 轉回 int64
		roleIDInt64 := oidToInt64[rp.RoleID.Hex()]
		result[roleIDInt64] = append(result[roleIDInt64], rp)
	}

	return result, nil
}

// GetByPermissionIDs 根據權限 ID 取得所有角色關聯
func (repo *RolePermissionRepository) GetByPermissionIDs(ctx context.Context, permissionIDs []int64) ([]*entity.RolePermission, error) {
	if len(permissionIDs) == 0 {
		return []*entity.RolePermission{}, nil
	}

	var data []*entity.RolePermission

	// 將 int64 轉換為 ObjectID
	permOIDs := make([]bson.ObjectID, 0, len(permissionIDs))
	for _, permID := range permissionIDs {
		permOID := bson.NewObjectIDFromTimestamp(time.Unix(permID, 0))
		permOIDs = append(permOIDs, permOID)
	}

	filter := bson.M{
		"permission_id": bson.M{"$in": permOIDs},
	}

	err := repo.DB.GetClient().Find(ctx, &data, filter)
	if err != nil {
		if errors.Is(err, mon.ErrNotFound) {
			return []*entity.RolePermission{}, nil
		}
		return nil, err
	}

	return data, nil
}

// GetRolesByPermission 根據權限 ID 取得所有角色 ID
func (repo *RolePermissionRepository) GetRolesByPermission(ctx context.Context, permissionID int64) ([]int64, error) {
	var data []*entity.RolePermission

	// 將 int64 轉換為 ObjectID
	permOID := bson.NewObjectIDFromTimestamp(time.Unix(permissionID, 0))

	filter := bson.M{"permission_id": permOID}

	err := repo.DB.GetClient().Find(ctx, &data, filter)
	if err != nil {
		if errors.Is(err, mon.ErrNotFound) {
			return []int64{}, nil
		}
		return nil, err
	}

	// 提取所有 roleID 並轉換回 int64
	roleIDs := make([]int64, 0, len(data))
	for _, rp := range data {
		// 將 ObjectID 轉換回 int64 (取 timestamp)
		roleIDInt64 := rp.RoleID.Timestamp().Unix()
		roleIDs = append(roleIDs, roleIDInt64)
	}

	return roleIDs, nil
}

// clearRolePermissionCache 清除角色權限關聯快取
func (repo *RolePermissionRepository) clearRolePermissionCache(ctx context.Context, roleID int64) {
	rk := domain.GetRolePermissionRedisKey(roleID)
	_ = repo.DB.DelCache(ctx, rk)
}

// Index20251009003UP 建立 RolePermission 集合的索引
// 這個函數應該在應用啟動時或數據庫遷移時執行一次
func (repo *RolePermissionRepository) Index20251009003UP(ctx context.Context) (*mongodriver.Cursor, error) {
	// 1. 複合唯一索引：角色 ID + 權限 ID 的組合必須唯一（避免重複關聯）
	// 等價於 db.role_permission.createIndex({"role_id": 1, "permission_id": 1}, {unique: true})
	repo.DB.PopulateMultiIndex(ctx, []string{"role_id", "permission_id"}, []int32{1, 1}, true)

	// 2. 查詢索引：按角色 ID 查詢（用於獲取某角色的所有權限）
	// 等價於 db.role_permission.createIndex({"role_id": 1})
	repo.DB.PopulateIndex(ctx, "role_id", 1, false)

	// 3. 查詢索引：按權限 ID 查詢（用於獲取擁有某權限的所有角色）
	// 等價於 db.role_permission.createIndex({"permission_id": 1})
	repo.DB.PopulateIndex(ctx, "permission_id", 1, false)

	// 4. 複合索引：按權限 ID 和狀態查詢
	// 等價於 db.role_permission.createIndex({"permission_id": 1, "status": 1})
	repo.DB.PopulateMultiIndex(ctx, []string{"permission_id", "status"}, []int32{1, 1}, false)

	// 5. 時間戳索引：用於排序和時間範圍查詢
	// 等價於 db.role_permission.createIndex({"create_time": 1})
	repo.DB.PopulateIndex(ctx, "create_time", 1, false)

	// 返回所有索引列表
	return repo.DB.GetClient().Indexes().List(ctx)
}
