package controller

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/QuantumNous/new-api/common"
	"github.com/QuantumNous/new-api/model"
	"github.com/glebarez/sqlite"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/gorm"
)

func setupRedemptionControllerDB(t *testing.T) *gorm.DB {
	t.Helper()

	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	sqlDB, _ := db.DB()
	sqlDB.SetMaxOpenConns(1)

	origDB := model.DB
	origLogDB := model.LOG_DB
	model.DB = db
	model.LOG_DB = db
	common.UsingSQLite = true
	common.RedisEnabled = false

	require.NoError(t, db.AutoMigrate(&model.User{}, &model.Redemption{}, &model.Log{}))

	t.Cleanup(func() {
		model.DB = origDB
		model.LOG_DB = origLogDB
		_ = sqlDB.Close()
	})

	return db
}

func setupRedemptionRouter() *gin.Engine {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("id", 1)
		c.Next()
	})

	g := r.Group("/api/redemption")
	g.POST("/", AddRedemption)
	g.PUT("/", UpdateRedemption)

	return r
}

func TestAddRedemptionStoresRemark(t *testing.T) {
	db := setupRedemptionControllerDB(t)
	router := setupRedemptionRouter()

	body, err := json.Marshal(map[string]interface{}{
		"name":         "campaign-a",
		"remark":       "admin only note",
		"quota":        500000,
		"count":        1,
		"expired_time": 0,
	})
	require.NoError(t, err)

	req := httptest.NewRequest(http.MethodPost, "/api/redemption/", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var rows []model.Redemption
	require.NoError(t, db.Find(&rows).Error)
	require.Len(t, rows, 1)
	assert.Equal(t, "campaign-a", rows[0].Name)
	assert.Equal(t, "admin only note", rows[0].Remark)
}

func TestUpdateRedemptionStoresRemark(t *testing.T) {
	db := setupRedemptionControllerDB(t)
	router := setupRedemptionRouter()

	row := model.Redemption{
		Id:          1,
		UserId:      1,
		Key:         "update-remark-key",
		Name:        "campaign-b",
		Remark:      "before update",
		Status:      common.RedemptionCodeStatusEnabled,
		Quota:       500000,
		CreatedTime: common.GetTimestamp(),
	}
	require.NoError(t, db.Create(&row).Error)

	body, err := json.Marshal(map[string]interface{}{
		"id":           1,
		"name":         "campaign-b",
		"remark":       "after update",
		"quota":        500000,
		"expired_time": 0,
	})
	require.NoError(t, err)

	req := httptest.NewRequest(http.MethodPut, "/api/redemption/", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var stored model.Redemption
	require.NoError(t, db.First(&stored, 1).Error)
	assert.Equal(t, "after update", stored.Remark)
}

func TestUpdateRedemptionStatusOnlyKeepsRemark(t *testing.T) {
	db := setupRedemptionControllerDB(t)
	router := setupRedemptionRouter()

	row := model.Redemption{
		Id:          1,
		UserId:      1,
		Key:         "status-only-remark-key",
		Name:        "campaign-c",
		Remark:      "keep this remark",
		Status:      common.RedemptionCodeStatusEnabled,
		Quota:       500000,
		CreatedTime: common.GetTimestamp(),
	}
	require.NoError(t, db.Create(&row).Error)

	body, err := json.Marshal(map[string]interface{}{
		"id":     1,
		"status": common.RedemptionCodeStatusDisabled,
		"remark": "should not overwrite",
	})
	require.NoError(t, err)

	req := httptest.NewRequest(http.MethodPut, "/api/redemption/?status_only=true", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var stored model.Redemption
	require.NoError(t, db.First(&stored, 1).Error)
	assert.Equal(t, common.RedemptionCodeStatusDisabled, stored.Status)
	assert.Equal(t, "keep this remark", stored.Remark)
}
