package integ

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/bootstrap"
	"github.com/break/junhong_cmp_fiber/internal/gateway"
	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/routes"
	"github.com/break/junhong_cmp_fiber/pkg/auth"
	"github.com/break/junhong_cmp_fiber/pkg/config"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
	"github.com/break/junhong_cmp_fiber/tests/testutils"
	"github.com/gofiber/fiber/v2"
	"github.com/redis/go-redis/v9"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

// IntegrationTestEnv 集成测试环境
// 封装集成测试所需的所有依赖，提供统一的测试环境设置
type IntegrationTestEnv struct {
	TX           *gorm.DB           // 自动回滚的数据库事务
	Redis        *redis.Client      // 全局 Redis 连接
	Logger       *zap.Logger        // 测试用日志记录器
	TokenManager *auth.TokenManager // Token 管理器
	App          *fiber.App         // 配置好的 Fiber 应用实例
	Handlers     *bootstrap.Handlers
	Middlewares  *bootstrap.Middlewares

	t            *testing.T
	superAdmin   *model.Account
	currentToken string
}

// NewIntegrationTestEnv 创建集成测试环境
//
// 自动完成以下初始化：
//   - 创建独立的数据库事务（测试结束后自动回滚）
//   - 获取全局 Redis 连接并清理测试键
//   - 创建 Logger 和 TokenManager
//   - 通过 Bootstrap 初始化所有 Handlers 和 Middlewares
//   - 配置 Fiber App 并注册路由
//
// 用法:
//
//	func TestXxx(t *testing.T) {
//	    env := testutils.NewIntegrationTestEnv(t)
//	    // env.App 已配置好，可直接发送请求
//	    // env.TX 是独立事务，测试结束后自动回滚
//	}
var configOnce sync.Once

func NewIntegrationTestEnv(t *testing.T) *IntegrationTestEnv {
	t.Helper()

	configOnce.Do(func() {
		_, _ = config.Load()
	})

	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	logger, _ := zap.NewDevelopment()
	tokenManager := auth.NewTokenManager(rdb, 24*time.Hour, 7*24*time.Hour)

	gatewayClient := createMockGatewayClient()

	deps := &bootstrap.Dependencies{
		DB:            tx,
		Redis:         rdb,
		Logger:        logger,
		TokenManager:  tokenManager,
		GatewayClient: gatewayClient,
	}

	result, err := bootstrap.Bootstrap(deps)
	require.NoError(t, err, "Bootstrap 初始化失败")

	app := fiber.New(fiber.Config{
		ErrorHandler: errors.SafeErrorHandler(logger),
	})

	routes.RegisterRoutes(app, result.Handlers, result.Middlewares)

	env := &IntegrationTestEnv{
		TX:           tx,
		Redis:        rdb,
		Logger:       logger,
		TokenManager: tokenManager,
		App:          app,
		Handlers:     result.Handlers,
		Middlewares:  result.Middlewares,
		t:            t,
	}

	return env
}

func createMockGatewayClient() *gateway.Client {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		resp := gateway.GatewayResponse{
			Code:    200,
			Msg:     "success",
			TraceID: "test-trace-id",
			Data:    json.RawMessage(`{}`),
		}
		json.NewEncoder(w).Encode(resp)
	}))

	client := gateway.NewClient(server.URL, "test-app-id", "test-app-secret")
	return client
}

// AsSuperAdmin 设置当前请求使用超级管理员身份
// 返回 IntegrationTestEnv 以支持链式调用
//
// 用法:
//
//	resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/roles", nil)
func (e *IntegrationTestEnv) AsSuperAdmin() *IntegrationTestEnv {
	e.t.Helper()

	if e.superAdmin == nil {
		e.superAdmin = e.ensureSuperAdmin()
	}

	e.currentToken = e.generateToken(e.superAdmin)
	return e
}

// AsUser 设置当前请求使用指定用户身份
// 返回 IntegrationTestEnv 以支持链式调用
//
// 用法:
//
//	account := e.CreateTestAccount(...)
//	resp, err := env.AsUser(account).Request("GET", "/api/admin/shops", nil)
func (e *IntegrationTestEnv) AsUser(account *model.Account) *IntegrationTestEnv {
	e.t.Helper()

	token := e.generateToken(account)
	e.currentToken = token

	return e
}

// Request 发送 HTTP 请求
// 自动添加 Authorization header（如果已设置用户身份）
//
// 用法:
//
//	resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/roles", nil)
//	resp, err := env.Request("POST", "/api/admin/login", loginBody)
func (e *IntegrationTestEnv) Request(method, path string, body []byte) (*http.Response, error) {
	e.t.Helper()

	var req *http.Request
	if body != nil {
		req = httptest.NewRequest(method, path, bytes.NewReader(body))
		req.Header.Set("Content-Type", "application/json")
	} else {
		req = httptest.NewRequest(method, path, nil)
	}

	if e.currentToken != "" {
		req.Header.Set("Authorization", "Bearer "+e.currentToken)
	}

	return e.App.Test(req, -1)
}

// RequestWithHeaders 发送带自定义 Headers 的 HTTP 请求
func (e *IntegrationTestEnv) RequestWithHeaders(method, path string, body []byte, headers map[string]string) (*http.Response, error) {
	e.t.Helper()

	var req *http.Request
	if body != nil {
		req = httptest.NewRequest(method, path, bytes.NewReader(body))
	} else {
		req = httptest.NewRequest(method, path, nil)
	}

	for k, v := range headers {
		req.Header.Set(k, v)
	}

	if body != nil && req.Header.Get("Content-Type") == "" {
		req.Header.Set("Content-Type", "application/json")
	}

	if e.currentToken != "" && req.Header.Get("Authorization") == "" {
		req.Header.Set("Authorization", "Bearer "+e.currentToken)
	}

	return e.App.Test(req, -1)
}

// ClearAuth 清除当前认证状态
func (e *IntegrationTestEnv) ClearAuth() *IntegrationTestEnv {
	e.currentToken = ""
	return e
}

// ensureSuperAdmin 确保超级管理员账号存在
func (e *IntegrationTestEnv) ensureSuperAdmin() *model.Account {
	e.t.Helper()

	var existing model.Account
	err := e.TX.Where("user_type = ?", constants.UserTypeSuperAdmin).First(&existing).Error
	if err == nil {
		return &existing
	}

	return e.CreateTestAccount("superadmin", "password123", constants.UserTypeSuperAdmin, nil, nil)
}

// generateToken 为账号生成访问 Token
func (e *IntegrationTestEnv) generateToken(account *model.Account) string {
	e.t.Helper()

	ctx := context.Background()

	var shopID, enterpriseID uint
	if account.ShopID != nil {
		shopID = *account.ShopID
	}
	if account.EnterpriseID != nil {
		enterpriseID = *account.EnterpriseID
	}

	tokenInfo := &auth.TokenInfo{
		UserID:       account.ID,
		UserType:     account.UserType,
		ShopID:       shopID,
		EnterpriseID: enterpriseID,
		Username:     account.Username,
		Device:       "test",
		IP:           "127.0.0.1",
	}

	accessToken, _, err := e.TokenManager.GenerateTokenPair(ctx, tokenInfo)
	require.NoError(e.t, err, "生成 Token 失败")

	return accessToken
}

var (
	usernameCounter uint64
	phoneCounter    uint64
	shopCodeCounter uint64
)

// CreateTestAccount 创建测试账号
func (e *IntegrationTestEnv) CreateTestAccount(username, password string, userType int, shopID, enterpriseID *uint) *model.Account {
	e.t.Helper()

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	require.NoError(e.t, err)

	counter := atomic.AddUint64(&usernameCounter, 1)
	uniqueUsername := fmt.Sprintf("%s_%d", username, counter)
	uniquePhone := fmt.Sprintf("138%08d", atomic.AddUint64(&phoneCounter, 1))

	account := &model.Account{
		BaseModel: model.BaseModel{
			Creator: 1,
			Updater: 1,
		},
		Username:     uniqueUsername,
		Phone:        uniquePhone,
		Password:     string(hashedPassword),
		UserType:     userType,
		ShopID:       shopID,
		EnterpriseID: enterpriseID,
		Status:       1,
	}

	err = e.TX.Create(account).Error
	require.NoError(e.t, err, "创建测试账号失败")

	return account
}

// CreateTestShop 创建测试商户
func (e *IntegrationTestEnv) CreateTestShop(name string, level int, parentID *uint) *model.Shop {
	e.t.Helper()

	counter := atomic.AddUint64(&shopCodeCounter, 1)
	uniqueCode := fmt.Sprintf("SHOP_%d_%d", time.Now().UnixNano()%10000, counter)
	uniqueName := fmt.Sprintf("%s_%d", name, counter)

	shop := &model.Shop{
		BaseModel: model.BaseModel{
			Creator: 1,
			Updater: 1,
		},
		ShopName: uniqueName,
		ShopCode: uniqueCode,
		Level:    level,
		ParentID: parentID,
		Status:   1,
	}

	err := e.TX.Create(shop).Error
	require.NoError(e.t, err, "创建测试商户失败")

	return shop
}

// CreateTestEnterprise 创建测试企业
func (e *IntegrationTestEnv) CreateTestEnterprise(name string, ownerShopID *uint) *model.Enterprise {
	e.t.Helper()

	counter := atomic.AddUint64(&shopCodeCounter, 1)
	uniqueCode := fmt.Sprintf("ENT_%d_%d", time.Now().UnixNano()%10000, counter)
	uniqueName := fmt.Sprintf("%s_%d", name, counter)

	enterprise := &model.Enterprise{
		BaseModel: model.BaseModel{
			Creator: 1,
			Updater: 1,
		},
		EnterpriseName: uniqueName,
		EnterpriseCode: uniqueCode,
		OwnerShopID:    ownerShopID,
		Status:         1,
	}

	err := e.TX.Create(enterprise).Error
	require.NoError(e.t, err, "创建测试企业失败")

	return enterprise
}

// CreateTestRole 创建测试角色
func (e *IntegrationTestEnv) CreateTestRole(name string, roleType int) *model.Role {
	e.t.Helper()

	counter := atomic.AddUint64(&usernameCounter, 1)
	uniqueName := fmt.Sprintf("%s_%d", name, counter)

	role := &model.Role{
		BaseModel: model.BaseModel{
			Creator: 1,
			Updater: 1,
		},
		RoleName: uniqueName,
		RoleType: roleType,
		Status:   constants.StatusEnabled,
	}

	err := e.TX.Create(role).Error
	require.NoError(e.t, err, "创建测试角色失败")

	return role
}

// CreateTestPermission 创建测试权限
func (e *IntegrationTestEnv) CreateTestPermission(name, code string, permType int) *model.Permission {
	e.t.Helper()

	counter := atomic.AddUint64(&usernameCounter, 1)
	uniqueName := fmt.Sprintf("%s_%d", name, counter)
	uniqueCode := fmt.Sprintf("%s_%d", code, counter)

	permission := &model.Permission{
		BaseModel: model.BaseModel{
			Creator: 1,
			Updater: 1,
		},
		PermName: uniqueName,
		PermCode: uniqueCode,
		PermType: permType,
		Status:   constants.StatusEnabled,
	}

	err := e.TX.Create(permission).Error
	require.NoError(e.t, err, "创建测试权限失败")

	return permission
}

// SetUserContext 设置用户上下文（用于直接调用 Service 层测试）
func (e *IntegrationTestEnv) SetUserContext(ctx context.Context, userID uint, userType int, shopID uint) context.Context {
	return middleware.SetUserContext(ctx, middleware.NewSimpleUserContext(userID, userType, shopID))
}

// GetSuperAdminContext 获取超级管理员上下文
func (e *IntegrationTestEnv) GetSuperAdminContext() context.Context {
	if e.superAdmin == nil {
		e.superAdmin = e.ensureSuperAdmin()
	}
	return e.SetUserContext(context.Background(), e.superAdmin.ID, constants.UserTypeSuperAdmin, 0)
}

// RawDB 获取跳过数据权限过滤的数据库连接
// 用于测试中验证数据是否正确写入，不受 GORM Callback 影响
//
// 用法:
//
//	var count int64
//	env.RawDB().Model(&model.Role{}).Where("role_name = ?", name).Count(&count)
func (e *IntegrationTestEnv) RawDB() *gorm.DB {
	ctx := e.GetSuperAdminContext()
	return e.TX.WithContext(ctx)
}
