package middlewares

import (
	"quyun/v2/app/errorx"
	"quyun/v2/app/services"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"
	"quyun/v2/providers/jwt"

	"github.com/gofiber/fiber/v3"
)

func (f *Middlewares) TenantResolve(c fiber.Ctx) error {
	tenantCode := c.Params("tenantCode")
	if tenantCode == "" {
		return errorx.ErrMissingParameter.WithMsg("缺少 tenantCode")
	}

	tenantModel, err := services.Tenant.FindByCode(c, tenantCode)
	if err != nil {
		f.log.WithField("tenant_code", tenantCode).WithError(err).Warn("middlewares.tenant.resolve.failed")
		return err
	}

	f.log.WithFields(map[string]any{
		"tenant_id":   tenantModel.ID,
		"tenant_code": tenantCode,
	}).Info("middlewares.tenant.resolve.ok")

	c.Locals(consts.CtxKeyTenant, tenantModel)
	return c.Next()
}

func (f *Middlewares) TenantAuth(c fiber.Ctx) error {
	authHeader := c.Get(jwt.HttpHeader)
	if authHeader == "" {
		f.log.Info("middlewares.tenant.auth.missing_token")
		return errorx.ErrTokenMissing
	}

	claims, err := f.jwt.Parse(authHeader)
	if err != nil {
		f.log.WithError(err).Warn("middlewares.tenant.auth.invalid_token")
		switch err {
		case jwt.TokenExpired:
			return errorx.ErrTokenExpired
		case jwt.TokenMalformed, jwt.TokenNotValidYet, jwt.TokenInvalid:
			return errorx.ErrTokenInvalid
		default:
			return errorx.ErrTokenInvalid
		}
	}
	if claims.UserID == 0 {
		f.log.Warn("middlewares.tenant.auth.missing_user_id")
		return errorx.ErrTokenInvalid
	}

	f.log.WithFields(map[string]any{
		"user_id": claims.UserID,
	}).Info("middlewares.tenant.auth.ok")

	c.Locals(consts.CtxKeyClaims, claims)
	return c.Next()
}

func (f *Middlewares) TenantRequireMember(c fiber.Ctx) error {
	tenantModel, ok := c.Locals(consts.CtxKeyTenant).(*models.Tenant)
	if !ok || tenantModel == nil {
		f.log.Error("middlewares.tenant.require_member.missing_tenant_context")
		return errorx.ErrInternalError.WithMsg("tenant context missing")
	}

	claims, ok := c.Locals(consts.CtxKeyClaims).(*jwt.Claims)
	if !ok || claims == nil {
		f.log.Error("middlewares.tenant.require_member.missing_claims_context")
		return errorx.ErrInternalError.WithMsg("claims context missing")
	}

	tenantUser, err := services.Tenant.FindTenantUser(c, tenantModel.ID, claims.UserID)
	if err != nil {
		f.log.WithFields(map[string]any{
			"tenant_id": tenantModel.ID,
			"user_id":   claims.UserID,
		}).WithError(err).Warn("middlewares.tenant.require_member.denied")
		return errorx.ErrPermissionDenied.WithMsg("不属于该租户")
	}

	userModel, err := services.User.FindByID(c, claims.UserID)
	if err != nil {
		f.log.WithField("user_id", claims.UserID).WithError(err).Warn("middlewares.tenant.require_member.load_user_failed")
		return err
	}

	f.log.WithFields(map[string]any{
		"tenant_id": tenantModel.ID,
		"user_id":   claims.UserID,
	}).Info("middlewares.tenant.require_member.ok")

	c.Locals(consts.CtxKeyTenantUser, tenantUser)
	c.Locals(consts.CtxKeyUser, userModel)
	return c.Next()
}
