package middleware

import (
	"context"
	"strings"

	"github.com/break/junhong_cmp_fiber/pkg/auth"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	pkgmiddleware "github.com/break/junhong_cmp_fiber/pkg/middleware"
	"github.com/gofiber/fiber/v2"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
)

// PersonalAuthMiddleware 个人客户认证中间件
type PersonalAuthMiddleware struct {
	jwtManager *auth.JWTManager
	redis      *redis.Client
	logger     *zap.Logger
}

// NewPersonalAuthMiddleware 创建个人客户认证中间件
func NewPersonalAuthMiddleware(jwtManager *auth.JWTManager, rdb *redis.Client, logger *zap.Logger) *PersonalAuthMiddleware {
	return &PersonalAuthMiddleware{
		jwtManager: jwtManager,
		redis:      rdb,
		logger:     logger,
	}
}

// Authenticate 认证中间件
// JWT + Redis 双重校验：先验证 JWT 签名和有效期，再检查 Redis 中 token 是否存在
func (m *PersonalAuthMiddleware) Authenticate() fiber.Handler {
	return func(c *fiber.Ctx) error {
		if c.Method() == fiber.MethodOptions {
			return c.SendStatus(fiber.StatusNoContent)
		}

		authHeader := c.Get("Authorization")
		if authHeader == "" {
			m.logger.Warn("个人客户认证失败：缺少 Authorization header",
				zap.String("path", c.Path()),
				zap.String("method", c.Method()),
			)
			return errors.New(errors.CodeUnauthorized, "未提供认证令牌")
		}

		parts := strings.SplitN(authHeader, " ", 2)
		if len(parts) != 2 || parts[0] != "Bearer" {
			m.logger.Warn("个人客户认证失败：Authorization header 格式错误",
				zap.String("path", c.Path()),
				zap.String("auth_header", authHeader),
			)
			return errors.New(errors.CodeUnauthorized, "认证令牌格式错误")
		}

		token := parts[1]

		claims, err := m.jwtManager.VerifyPersonalCustomerToken(token)
		if err != nil {
			m.logger.Warn("个人客户认证失败：token 验证失败",
				zap.String("path", c.Path()),
				zap.Error(err),
			)
			return errors.New(errors.CodeUnauthorized, "认证令牌无效或已过期")
		}

		// Redis 有效性检查：token 必须在 Redis 中存在才视为有效
		// 支持服务端主动失效（封禁/强制下线/退出登录）
		redisKey := constants.RedisPersonalCustomerTokenKey(claims.CustomerID)
		storedToken, redisErr := m.redis.Get(context.Background(), redisKey).Result()
		if redisErr == redis.Nil {
			m.logger.Warn("个人客户认证失败：token 已被服务端失效",
				zap.Uint("customer_id", claims.CustomerID),
				zap.String("path", c.Path()),
			)
			return errors.New(errors.CodeUnauthorized, "认证令牌已失效，请重新登录")
		}
		if redisErr != nil {
			m.logger.Error("个人客户认证：Redis 查询异常",
				zap.Uint("customer_id", claims.CustomerID),
				zap.Error(redisErr),
			)
			return errors.New(errors.CodeUnauthorized, "认证服务异常，请稍后重试")
		}
		// 比对 Redis 中存储的 token 与当前请求 token 是否一致
		if storedToken != token {
			m.logger.Warn("个人客户认证失败：token 不匹配（可能已在其他设备登录）",
				zap.Uint("customer_id", claims.CustomerID),
				zap.String("path", c.Path()),
			)
			return errors.New(errors.CodeUnauthorized, "认证令牌已失效，请重新登录")
		}

		c.Locals("customer_id", claims.CustomerID)
		c.Locals("customer_phone", claims.Phone)
		c.Locals("customer_asset_type", claims.AssetType)
		c.Locals("customer_asset_id", claims.AssetID)
		c.Locals("skip_owner_filter", true)
		c.SetUserContext(pkgmiddleware.SetUserContext(c.UserContext(), &pkgmiddleware.UserContextInfo{
			CustomerID: claims.CustomerID,
		}))

		m.logger.Debug("个人客户认证成功",
			zap.Uint("customer_id", claims.CustomerID),
			zap.String("phone", claims.Phone),
			zap.String("path", c.Path()),
		)

		return c.Next()
	}
}

// GetCustomerID 从 context 中获取当前个人客户 ID
func GetCustomerID(c *fiber.Ctx) (uint, bool) {
	customerID, ok := c.Locals("customer_id").(uint)
	return customerID, ok
}

// GetCustomerPhone 从 context 中获取当前个人客户手机号
func GetCustomerPhone(c *fiber.Ctx) (string, bool) {
	phone, ok := c.Locals("customer_phone").(string)
	return phone, ok
}

// GetCurrentAsset 从 context 中获取当前登录所用的资产类型和 ID
func GetCurrentAsset(c *fiber.Ctx) (assetType string, assetID uint, ok bool) {
	assetType, _ = c.Locals("customer_asset_type").(string)
	assetID, _ = c.Locals("customer_asset_id").(uint)
	ok = assetType != "" && assetID != 0
	return
}
