package repository

import (
	"context"
	"fmt"
	"testing"
	"time"

	"backend/pkg/member/domain/entity"
	"backend/pkg/member/domain/repository"

	mgo "backend/pkg/library/mongo"

	"github.com/stretchr/testify/assert"
)

func SetupTestAutoIDRepository(db string) (repository.AutoIDRepository, func(), error) {
	h, p, tearDown, err := startMongoContainer()
	if err != nil {
		return nil, nil, err
	}

	conf := &mgo.Conf{
		Schema:                           Schema,
		Host:                             fmt.Sprintf("%s:%s", h, p),
		Database:                         db,
		MaxStaleness:                     300,
		MaxPoolSize:                      100,
		MinPoolSize:                      100,
		MaxConnIdleTime:                  300,
		Compressors:                      []string{},
		EnableStandardReadWriteSplitMode: false,
		ConnectTimeoutMs:                 3000,
	}

	param := AutoIDRepositoryParam{
		Conf: conf,
	}

	repo := NewAutoIDRepository(param)
	_, _ = repo.Index20241226001UP(context.Background())

	return repo, tearDown, nil
}

func TestDefaultAutoIDModel_Insert(t *testing.T) {
	repo, tearDown, err := SetupTestAutoIDRepository("testDB")
	defer tearDown()
	assert.NoError(t, err)

	tests := []struct {
		name        string
		data        *entity.AutoID
		expectError bool
	}{
		{
			name: "Valid AutoID insert",
			data: &entity.AutoID{
				Name:    "testCounter",
				Counter: 100,
			},
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := repo.Insert(context.Background(), tt.data)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err, "插入操作應該成功")

				// 檢查生成的 ID 和時間戳
				assert.NotZero(t, tt.data.ID, "ID 應該被生成")
				assert.NotNil(t, tt.data.CreateAt, "CreateAt 應該被設置")
				assert.NotNil(t, tt.data.UpdateAt, "UpdateAt 應該被設置")

				// 確認插入的資料是否正確
				now := time.Now().UTC().UnixNano()
				assert.LessOrEqual(t, *tt.data.CreateAt, now, "CreateAt 應在當前時間之前")
				assert.LessOrEqual(t, *tt.data.UpdateAt, now, "UpdateAt 應在當前時間之前")

				insertedAutoID, err := repo.FindOne(context.Background(), tt.data.ID.Hex())
				assert.NoError(t, err, "應該可以找到插入的資料")
				assert.Equal(t, tt.data.Name, insertedAutoID.Name, "Name 應相同")
				assert.Equal(t, tt.data.Counter, insertedAutoID.Counter, "Counter 應相同")
			}
		})
	}
}

func TestDefaultAutoIDModel_FindOne(t *testing.T) {
	repo, tearDown, err := SetupTestAutoIDRepository("testDB")
	defer tearDown()
	assert.NoError(t, err)

	inserted := &entity.AutoID{
		Name:    "findTestCounter",
		Counter: 200,
	}
	err = repo.Insert(context.Background(), inserted)
	assert.NoError(t, err)

	tests := []struct {
		name        string
		id          string
		expectError bool
		expectedID  string
	}{
		{
			name:        "Valid FindOne by ID",
			id:          inserted.ID.Hex(),
			expectError: false,
			expectedID:  inserted.ID.Hex(),
		},
		{
			name:        "Invalid ObjectID",
			id:          "invalidObjectID",
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := repo.FindOne(context.Background(), tt.id)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Equal(t, tt.expectedID, result.ID.Hex(), "ID 應相同")
			}
		})
	}
}

func TestDefaultAutoIDModel_Update(t *testing.T) {
	repo, tearDown, err := SetupTestAutoIDRepository("testDB")
	defer tearDown()
	assert.NoError(t, err)

	inserted := &entity.AutoID{
		Name:    "updateTestCounter",
		Counter: 300,
	}
	err = repo.Insert(context.Background(), inserted)
	assert.NoError(t, err)

	updatedData := *inserted // 創建副本進行更新
	updatedData.Counter = 400

	tests := []struct {
		name        string
		data        *entity.AutoID
		expectError bool
	}{
		{
			name:        "Valid Update",
			data:        &updatedData,
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := repo.Update(context.Background(), tt.data)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				// 驗證更新後的 Counter
				updatedAutoID, err := repo.FindOne(context.Background(), tt.data.ID.Hex())
				assert.NoError(t, err)
				assert.Equal(t, tt.data.Counter, updatedAutoID.Counter, "Counter 應相同")
			}
		})
	}
}

func TestDefaultAutoIDModel_Delete(t *testing.T) {
	repo, tearDown, err := SetupTestAutoIDRepository("testDB")
	defer tearDown()
	assert.NoError(t, err)

	inserted := &entity.AutoID{
		Name:    "deleteTestCounter",
		Counter: 500,
	}
	err = repo.Insert(context.Background(), inserted)
	assert.NoError(t, err)

	tests := []struct {
		name        string
		id          string
		expectError bool
	}{
		{
			name:        "Valid Delete by ID",
			id:          inserted.ID.Hex(),
			expectError: false,
		},
		{
			name:        "Invalid ObjectID",
			id:          "invalidObjectID",
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			count, err := repo.Delete(context.Background(), tt.id)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Equal(t, int64(1), count, "刪除應影響 1 條記錄")

				// 確認已刪除
				_, err = repo.FindOne(context.Background(), tt.id)
				assert.ErrorIs(t, err, ErrNotFound, "記錄應該不存在")
			}
		})
	}
}

func TestCustomAutoIDModel_Inc(t *testing.T) {

	tests := []struct {
		name        string
		initialData *entity.AutoID
		expectError bool
	}{
		{
			name:        "Increment non-existing counter (upsert)",
			initialData: nil, // 不提供初始數據，預期會自動插入新數據
			expectError: false,
		},
		{
			name: "Increment existing counter",
			initialData: &entity.AutoID{
				Name:    "auto_id",
				Counter: 5,
			},
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			repo, tearDown, err := SetupTestAutoIDRepository("testDB")
			defer tearDown()
			assert.NoError(t, err)
			// 插入初始數據（如果有）
			if tt.initialData != nil {
				err := repo.Insert(context.Background(), tt.initialData)
				assert.NoError(t, err, "初始插入操作應該成功")
			}

			// 創建一個 AutoID 結構來保存增量操作後的結果
			var result entity.AutoID

			// 執行 Inc 方法
			err = repo.Inc(context.Background(), &result)
			if tt.expectError {
				assert.Error(t, err, "應該返回錯誤")
			} else {
				assert.NoError(t, err, "Inc 操作應該成功")

				// 驗證結果中的 Counter 值是否正確增量
				expectedCounter := uint64(1)
				if tt.initialData != nil {
					expectedCounter = tt.initialData.Counter + 1
				}

				assert.Equal(t, expectedCounter, result.Counter, "Counter 值應該自增")

			}
		})
	}
}
