// repo_bench_test.go — repository 层性能基准测试
// 覆盖：批量写入、并发只读查询、分页列表、更新状态、软删除
package repository

import (
	"context"
	"fmt"
	"sync"
	"sync/atomic"
	"testing"

	_ "modernc.org/sqlite"
	gormsqlite "gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"

	"github.com/user-management-system/internal/domain"
)

var repoBenchCounter int64

// openBenchDB 为 Benchmark 打开独立内存 DB（不依赖 *testing.T）
func openBenchDB(b *testing.B) *gorm.DB {
	b.Helper()
	id := atomic.AddInt64(&repoBenchCounter, 1)
	dsn := fmt.Sprintf("file:repobenchdb%d?mode=memory&cache=private", id)
	db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{
		DriverName: "sqlite",
		DSN:        dsn,
	}), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		b.Fatalf("openBenchDB: %v", err)
	}
	if err := db.AutoMigrate(
		&domain.User{},
		&domain.Role{},
		&domain.Permission{},
		&domain.UserRole{},
		&domain.RolePermission{},
	); err != nil {
		b.Fatalf("AutoMigrate: %v", err)
	}
	return db
}

// seedUsers 往 DB 插入 n 条用户
func seedUsers(b *testing.B, repo *UserRepository, n int) {
	b.Helper()
	ctx := context.Background()
	for i := 0; i < n; i++ {
		if err := repo.Create(ctx, &domain.User{
			Username: fmt.Sprintf("benchuser%06d", i),
			Email:    domain.StrPtr(fmt.Sprintf("bench%06d@example.com", i)),
			Phone:    domain.StrPtr(fmt.Sprintf("1380000%04d", i%10000)),
			Password: "hashed_placeholder",
			Status:   domain.UserStatusActive,
		}); err != nil {
			b.Fatalf("seedUsers i=%d: %v", i, err)
		}
	}
}

// ---------- BenchmarkRepo_Create — 单条写入吞吐 ----------

func BenchmarkRepo_Create(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	ctx := context.Background()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		repo.Create(ctx, &domain.User{ //nolint:errcheck
			Username: fmt.Sprintf("cr_%d_%d", b.N, i),
			Email:    domain.StrPtr(fmt.Sprintf("cr_%d_%d@bench.com", b.N, i)),
			Password: "hash",
			Status:   domain.UserStatusActive,
		})
	}
}

// ---------- BenchmarkRepo_BulkCreate — 批量写入（串行） ----------

func BenchmarkRepo_BulkCreate(b *testing.B) {
	sizes := []int{10, 100, 500}
	for _, size := range sizes {
		size := size
		b.Run(fmt.Sprintf("batch=%d", size), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				b.StopTimer()
				db := openBenchDB(b)
				repo := NewUserRepository(db)
				ctx := context.Background()
				users := make([]*domain.User, size)
				for j := 0; j < size; j++ {
					users[j] = &domain.User{
						Username: fmt.Sprintf("bulk_%d_%d_%d", i, j, size),
						Password: "hash",
						Status:   domain.UserStatusActive,
					}
				}
				b.StartTimer()
				for _, u := range users {
					repo.Create(ctx, u) //nolint:errcheck
				}
			}
		})
	}
}

// ---------- BenchmarkRepo_GetByID — 主键查询 ----------

func BenchmarkRepo_GetByID(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 1000)
	ctx := context.Background()
	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		id := int64(1)
		for pb.Next() {
			repo.GetByID(ctx, id) //nolint:errcheck
			id++
			if id > 1000 {
				id = 1
			}
		}
	})
}

// ---------- BenchmarkRepo_GetByUsername — 索引查询 ----------

func BenchmarkRepo_GetByUsername(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 500)
	ctx := context.Background()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		repo.GetByUsername(ctx, fmt.Sprintf("benchuser%06d", i%500)) //nolint:errcheck
	}
}

// ---------- BenchmarkRepo_GetByEmail — 索引查询 ----------

func BenchmarkRepo_GetByEmail(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 500)
	ctx := context.Background()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		repo.GetByEmail(ctx, fmt.Sprintf("bench%06d@example.com", i%500)) //nolint:errcheck
	}
}

// ---------- BenchmarkRepo_List — 分页列表 ----------

func BenchmarkRepo_List(b *testing.B) {
	pageSizes := []int{10, 50, 200}
	for _, ps := range pageSizes {
		ps := ps
		b.Run(fmt.Sprintf("pageSize=%d", ps), func(b *testing.B) {
			db := openBenchDB(b)
			repo := NewUserRepository(db)
			seedUsers(b, repo, 1000)
			ctx := context.Background()
			b.ResetTimer()

			for i := 0; i < b.N; i++ {
				repo.List(ctx, 0, ps) //nolint:errcheck
			}
		})
	}
}

// ---------- BenchmarkRepo_ListByStatus ----------

func BenchmarkRepo_ListByStatus(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 1000)
	ctx := context.Background()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		repo.ListByStatus(ctx, domain.UserStatusActive, 0, 20) //nolint:errcheck
	}
}

// ---------- BenchmarkRepo_UpdateStatus ----------

func BenchmarkRepo_UpdateStatus(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 200)
	ctx := context.Background()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		id := int64(i%200) + 1
		repo.UpdateStatus(ctx, id, domain.UserStatusActive) //nolint:errcheck
	}
}

// ---------- BenchmarkRepo_Update — 全字段更新 ----------

func BenchmarkRepo_Update(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 100)
	ctx := context.Background()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		id := int64(i%100) + 1
		u, err := repo.GetByID(ctx, id)
		if err != nil {
			continue
		}
		u.Nickname = fmt.Sprintf("nick%d", i)
		repo.Update(ctx, u) //nolint:errcheck
	}
}

// ---------- BenchmarkRepo_Delete — 软删除 ----------

func BenchmarkRepo_Delete(b *testing.B) {
	ctx := context.Background()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		b.StopTimer()
		db := openBenchDB(b)
		repo := NewUserRepository(db)
		repo.Create(ctx, &domain.User{Username: "victim", Password: "hash", Status: domain.UserStatusActive}) //nolint:errcheck
		b.StartTimer()
		repo.Delete(ctx, 1) //nolint:errcheck
	}
}

// ---------- BenchmarkRepo_ExistsByUsername ----------

func BenchmarkRepo_ExistsByUsername(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 500)
	ctx := context.Background()
	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		i := 0
		for pb.Next() {
			repo.ExistsByUsername(ctx, fmt.Sprintf("benchuser%06d", i%500)) //nolint:errcheck
			i++
		}
	})
}

// ---------- BenchmarkRepo_ConcurrentReadWrite — 高并发读写混合 ----------

func BenchmarkRepo_ConcurrentReadWrite(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 200)
	ctx := context.Background()

	var mu sync.Mutex // SQLite 不支持多写并发，需要序列化写入
	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		i := int64(1)
		for pb.Next() {
			if i%10 == 0 {
				// 10% 写操作
				mu.Lock()
				repo.UpdateLastLogin(ctx, i%200+1, "10.0.0.1") //nolint:errcheck
				mu.Unlock()
			} else {
				// 90% 读操作
				repo.GetByID(ctx, i%200+1) //nolint:errcheck
			}
			i++
		}
	})
}

// ---------- BenchmarkRepo_Search — 模糊搜索 ----------

func BenchmarkRepo_Search(b *testing.B) {
	db := openBenchDB(b)
	repo := NewUserRepository(db)
	seedUsers(b, repo, 2000)
	ctx := context.Background()
	b.ResetTimer()

	keywords := []string{"benchuser000", "bench0001", "benchuser05"}
	for i := 0; i < b.N; i++ {
		repo.Search(ctx, keywords[i%len(keywords)], 0, 20) //nolint:errcheck
	}
}
