package controllers

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"

	"GoMembership/internal/config"
	"GoMembership/internal/constants"
	"GoMembership/internal/middlewares"
	"GoMembership/internal/models"
	"GoMembership/internal/utils"
	"GoMembership/pkg/logger"
)

type loginInput struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

type RegisterUserTest struct {
	WantDBData   map[string]interface{}
	Name         string
	Input        string
	WantResponse int
	Assert       bool
}

func (rt *RegisterUserTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder, *gin.Engine) {
	return GetMockedJSONContext([]byte(rt.Input), "register")
}

func (rt *RegisterUserTest) RunHandler(c *gin.Context, router *gin.Engine) {
	Uc.RegisterUser(c)
}

func (rt *RegisterUserTest) ValidateResponse(w *httptest.ResponseRecorder) error {
	if w.Code != rt.WantResponse {
		responseBody, _ := io.ReadAll(w.Body)
		return fmt.Errorf("Register User: Didn't get the expected response code: got: %v; expected: %v. Context: %#v", w.Code, rt.WantResponse, string(responseBody))
	}
	return nil
}

func (rt *RegisterUserTest) ValidateResult() error {
	return validateUser(rt.Assert, rt.WantDBData)
}

func testUserController(t *testing.T) {

	tests := getTestUsers()
	for _, tt := range tests {
		t.Run(tt.Name, func(t *testing.T) {
			if err := runSingleTest(&tt); err != nil {
				t.Fatalf("Test failed: %v", err.Error())
			}
		})
	}
	testCurrentUserHandler(t)
}

func testLogoutHandler(t *testing.T) {
	loginCookie := testCurrentUserHandler(t)

	tests := []struct {
		name           string
		setupCookie    func(*http.Request)
		expectedStatus int
	}{
		{
			name: "Logout with valid cookie",
			setupCookie: func(req *http.Request) {
				req.AddCookie(&loginCookie)
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:           "Logout without cookie",
			setupCookie:    func(req *http.Request) {},
			expectedStatus: http.StatusOK, // Logout should still succeed even without a cookie
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gin.SetMode(gin.TestMode)
			router := gin.New()
			router.POST("/logout", Uc.LogoutHandler)

			w := httptest.NewRecorder()
			req, _ := http.NewRequest("POST", "/logout", nil)
			tt.setupCookie(req)

			router.ServeHTTP(w, req)

			assert.Equal(t, tt.expectedStatus, w.Code)

			var response map[string]string
			err := json.Unmarshal(w.Body.Bytes(), &response)
			assert.NoError(t, err)
			assert.Equal(t, "Logged out successfully", response["message"])

			// Check if the cookie has been cleared
			var logoutCookie *http.Cookie
			for _, cookie := range w.Result().Cookies() {
				if cookie.Name == "jwt" {
					logoutCookie = cookie
					break
				}
			}
			assert.NotNil(t, logoutCookie, "Logout should set a clearing cookie")
			assert.Equal(t, "", logoutCookie.Value, "Logout cookie should have empty value")
			assert.True(t, logoutCookie.Expires.Before(time.Now()), "Logout cookie should be expired")

			// Verify that the user can no longer access protected routes
			w = httptest.NewRecorder()
			req, _ = http.NewRequest("GET", "/current-user", nil)
			if logoutCookie != nil {
				req.AddCookie(logoutCookie)
			}
			router.GET("/current-user", middlewares.AuthMiddleware(), Uc.CurrentUserHandler)
			router.ServeHTTP(w, req)
			assert.Equal(t, http.StatusUnauthorized, w.Code, "User should not be able to access protected routes after logout")
		})
	}
}

func testLoginHandler(t *testing.T) (string, http.Cookie) {
	// This test should run after the user registration test
	var loginCookie http.Cookie
	var loginInput loginInput
	t.Run("LoginHandler", func(t *testing.T) {
		// Test cases
		tests := []struct {
			name           string
			input          string
			wantStatusCode int
			wantToken      bool
		}{
			{
				name: "Valid login",
				input: `{
                    "email": "john.doe@example.com",
                    "password": "password123"
                }`,
				wantStatusCode: http.StatusOK,
				wantToken:      true,
			},
			{
				name: "Invalid email",
				input: `{
                    "email": "nonexistent@example.com",
                    "password": "password123"
                }`,
				wantStatusCode: http.StatusNotFound,
				wantToken:      false,
			},
			{
				name: "Invalid password",
				input: `{
                    "email": "john.doe@example.com",
                    "password": "wrongpassword"
                }`,
				wantStatusCode: http.StatusNotAcceptable,
				wantToken:      false,
			},
		}

		for _, tt := range tests {
			logger.Error.Print("==============================================================")
			logger.Error.Printf("Testing : %v", tt.name)
			logger.Error.Print("==============================================================")
			t.Run(tt.name, func(t *testing.T) {
				// Setup
				c, w, _ := GetMockedJSONContext([]byte(tt.input), "/login")

				// Execute
				Uc.LoginHandler(c)

				// Assert
				assert.Equal(t, tt.wantStatusCode, w.Code)

				var response map[string]interface{}
				err := json.Unmarshal(w.Body.Bytes(), &response)
				assert.NoError(t, err)

				if tt.wantToken {
					logger.Info.Printf("Response: %#v", response)
					assert.Contains(t, response, "set-token")
					assert.NotEmpty(t, response["set-token"])
					for _, cookie := range w.Result().Cookies() {
						if cookie.Name == "jwt" {
							loginCookie = *cookie

							err = json.Unmarshal([]byte(tt.input), &loginInput)
							assert.NoError(t, err, "Failed to unmarshal input JSON")

							break
						}
					}
					assert.NotEmpty(t, loginCookie)
				} else {
					assert.NotContains(t, response, "set-token")
				}
			})

		}
	})
	return loginInput.Email, loginCookie
}

func testCurrentUserHandler(t *testing.T) http.Cookie {
	loginEmail, loginCookie := testLoginHandler(t)
	// This test should run after the user login test
	invalidCookie := http.Cookie{
		Name:  "jwt",
		Value: "invalid.token.here",
	}
	tests := []struct {
		name             string
		setupCookie      func(*http.Request)
		expectedUserMail string
		expectedStatus   int
	}{
		{
			name: "With valid cookie",
			setupCookie: func(req *http.Request) {
				req.AddCookie(&loginCookie)
			},
			expectedUserMail: loginEmail,
			expectedStatus:   http.StatusOK,
		},
		{
			name:           "Without cookie",
			setupCookie:    func(req *http.Request) {},
			expectedStatus: http.StatusUnauthorized,
		},
		{
			name: "With invalid cookie",
			setupCookie: func(req *http.Request) {
				req.AddCookie(&invalidCookie)
			},
			expectedStatus: http.StatusUnauthorized,
		},
	}

	for _, tt := range tests {
		logger.Error.Print("==============================================================")
		logger.Error.Printf("Testing : %v", tt.name)
		logger.Error.Print("==============================================================")
		if tt.expectedStatus == http.StatusOK {
			time.Sleep(time.Second) // Small delay to ensure different timestamps to get a different JWT token
		}
		t.Run(tt.name, func(t *testing.T) {

			gin.SetMode(gin.TestMode)
			router := gin.New()
			router.Use(middlewares.AuthMiddleware())
			router.GET("/current-user", Uc.CurrentUserHandler)

			w := httptest.NewRecorder()
			req, _ := http.NewRequest("GET", "/current-user", nil)
			tt.setupCookie(req)

			router.ServeHTTP(w, req)

			assert.Equal(t, tt.expectedStatus, w.Code)

			if tt.expectedStatus == http.StatusOK {
				var response models.User
				err := json.Unmarshal(w.Body.Bytes(), &response)
				assert.NoError(t, err)

				assert.Equal(t, tt.expectedUserMail, response.Email)
				var newCookie *http.Cookie
				for _, cookie := range w.Result().Cookies() {
					if cookie.Name == "jwt" {
						newCookie = cookie
						break
					}
				}
				assert.NotNil(t, newCookie, "Cookie should be renewed")
				assert.NotEqual(t, loginCookie.Value, newCookie.Value, "Cookie value should be different")
				assert.True(t, newCookie.MaxAge > 0, "New cookie should not be expired")
			} else {
				// For unauthorized requests, check for an error message
				var errorResponse map[string]string
				err := json.Unmarshal(w.Body.Bytes(), &errorResponse)
				assert.NoError(t, err)
				assert.Contains(t, errorResponse, "error")
				assert.NotEmpty(t, errorResponse["error"])
			}

		})
	}
	return loginCookie
}

func validateUser(assert bool, wantDBData map[string]interface{}) error {
	users, err := Uc.Service.GetUsers(wantDBData)
	if err != nil {
		return fmt.Errorf("Error in database ops: %#v", err)
	}

	if assert != (len(*users) != 0) {
		return fmt.Errorf("User entry query didn't met expectation: %v != %#v", assert, *users)
	}

	if assert {
		//check for email delivery
		messages := utils.SMTPGetMessages()
		for _, message := range messages {
			mail, err := utils.DecodeMail(message.MsgRequest())
			if err != nil {
				return err
			}
			if strings.Contains(mail.Subject, constants.MailRegistrationSubject) {
				if err := checkRegistrationMail(mail, &(*users)[0]); err != nil {
					return err
				}
			} else if strings.Contains(mail.Subject, constants.MailVerificationSubject) {
				if err := checkVerificationMail(mail, &(*users)[0]); err != nil {
					return err
				}
				verifiedUsers, err := Uc.Service.GetUsers(wantDBData)
				if err != nil {
					return err
				}
				if (*verifiedUsers)[0].Status != constants.VerifiedStatus {
					return fmt.Errorf("Users status isn't verified after email verification. Status is: %#v", (*verifiedUsers)[0].Status)
				}
			} else {
				return fmt.Errorf("Subject not expected: %v", mail.Subject)
			}
		}
	}
	return nil
}

func checkWelcomeMail(message *utils.Email, user *models.User) error {

	if !strings.Contains(message.To, user.Email) {
		return fmt.Errorf("Registration Information didn't reach the user! Recipient was: %v instead of %v", message.To, user.Email)
	}
	if !strings.Contains(message.From, config.SMTP.User) {
		return fmt.Errorf("Registration Information was sent from unexpected address! Sender was: %v instead of %v", message.From, config.SMTP.User)
	}

	//Check if all the relevant data has been passed to the mail.
	if !strings.Contains(message.Body, user.FirstName) {
		return fmt.Errorf("User first name(%v) has not been rendered in registration mail.", user.FirstName)
	}
	if !strings.Contains(message.Body, fmt.Sprintf("Preis/Monat</strong>: %v", user.Membership.SubscriptionModel.MonthlyFee)) {
		return fmt.Errorf("Users monthly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.MonthlyFee)
	}
	if !strings.Contains(message.Body, fmt.Sprintf("Preis/h</strong>: %v", user.Membership.SubscriptionModel.HourlyRate)) {
		return fmt.Errorf("Users hourly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.HourlyRate)
	}
	if user.Company != "" && !strings.Contains(message.Body, user.Company) {
		return fmt.Errorf("Users Company(%v) has not been rendered in registration mail.", user.Company)
	}
	if !strings.Contains(message.Body, fmt.Sprintf("Mitgliedsnummer</strong>: %v", user.Membership.ID)) {
		return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID)
	}
	if !strings.Contains(message.Body, config.Site.BaseURL) {
		return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.Site.BaseURL)
	}
	if !strings.Contains(message.Body, config.Site.BaseURL+config.Templates.LogoURI) {
		return fmt.Errorf("Logo Url (%v) has not been rendered in registration mail.", config.Site.BaseURL+config.Site.WebsiteTitle)
	}
	if !strings.Contains(message.Body, config.Site.WebsiteTitle) {
		return fmt.Errorf("Website title (%v) has not been rendered in registration mail.", config.Site.WebsiteTitle)
	}
	return nil
}

func checkRegistrationMail(message *utils.Email, user *models.User) error {

	if !strings.Contains(message.To, config.Recipients.UserRegistration) {
		return fmt.Errorf("Registration Information didn't reach admin! Recipient was: %v instead of %v", message.To, config.Recipients.UserRegistration)
	}
	if !strings.Contains(message.From, config.SMTP.User) {
		return fmt.Errorf("Registration Information was sent from unexpected address! Sender was: %v instead of %v", message.From, config.SMTP.User)
	}
	//Check if all the relevant data has been passed to the mail.
	if !strings.Contains(message.Body, user.FirstName+" "+user.LastName) {
		return fmt.Errorf("User first and last name(%v) has not been rendered in registration mail.", user.FirstName+" "+user.LastName)
	}
	if !strings.Contains(message.Body, fmt.Sprintf("Preis/Monat</strong>: %v", user.Membership.SubscriptionModel.MonthlyFee)) {
		return fmt.Errorf("Users monthly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.MonthlyFee)
	}
	if !strings.Contains(message.Body, fmt.Sprintf("Preis/h</strong>: %v", user.Membership.SubscriptionModel.HourlyRate)) {
		return fmt.Errorf("Users hourly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.HourlyRate)
	}
	if user.Company != "" && !strings.Contains(message.Body, user.Company) {
		return fmt.Errorf("Users Company(%v) has not been rendered in registration mail.", user.Company)
	}
	if !strings.Contains(message.Body, fmt.Sprintf("Mitgliedsnr:</strong> %v", user.Membership.ID)) {
		return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID)
	}
	if !strings.Contains(message.Body, user.Address+","+user.ZipCode) {
		return fmt.Errorf("Users address(%v) has not been rendered in registration mail.", user.Address+" sv,"+user.ZipCode)
	}
	if !strings.Contains(message.Body, user.City) {
		return fmt.Errorf("Users city(%v) has not been rendered in registration mail.", user.City)
	}
	if !strings.Contains(message.Body, user.DateOfBirth.Format("20060102")) {
		return fmt.Errorf("Users birthday(%v) has not been rendered in registration mail.", user.DateOfBirth.Format("20060102"))
	}
	if !strings.Contains(message.Body, "Email:</strong> "+user.Email) {
		return fmt.Errorf("Users email(%v) has not been rendered in registration mail.", user.Email)
	}
	if !strings.Contains(message.Body, user.Phone) {
		return fmt.Errorf("Users phone(%v) has not been rendered in registration mail.", user.Phone)
	}
	if !strings.Contains(message.Body, user.BankAccount.IBAN) {
		return fmt.Errorf("Users IBAN(%v) has not been rendered in registration mail.", user.BankAccount.IBAN)
	}
	if !strings.Contains(message.Body, config.Site.BaseURL) {
		return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.Site.BaseURL)
	}
	return nil
}

func checkVerificationMail(message *utils.Email, user *models.User) error {
	if !strings.Contains(message.To, user.Email) {
		return fmt.Errorf("Registration Information didn't reach client! Recipient was: %v instead of %v", message.To, user.Email)
	}
	verificationURL, err := getVerificationURL(message.Body)
	if err != nil {
		return fmt.Errorf("Error parsing verification URL: %#v", err.Error())
	}
	if !strings.Contains(verificationURL, user.Verification.VerificationToken) {
		return fmt.Errorf("Users Verification link token(%v) has not been rendered in email verification mail. %v", user.Verification.VerificationToken, verificationURL)
	}

	if !strings.Contains(message.Body, config.Site.BaseURL) {
		return fmt.Errorf("Base Url (%v) has not been rendered in email verification mail.", config.Site.BaseURL)
	}
	// open the provided link:
	if err := verifyMail(verificationURL); err != nil {
		return err
	}
	messages := utils.SMTPGetMessages()
	for _, message := range messages {
		mail, err := utils.DecodeMail(message.MsgRequest())
		if err != nil {
			return err
		}
		if err := checkWelcomeMail(mail, user); err != nil {
			return err
		}
	}
	return nil

}

func verifyMail(verificationURL string) error {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.LoadHTMLGlob(filepath.Join(config.Templates.HTMLPath, "*"))

	router.GET("/users/verify", Uc.VerifyMailHandler)
	wv := httptest.NewRecorder()
	cv, _ := gin.CreateTestContext(wv)
	var err error
	cv.Request, err = http.NewRequest("GET", verificationURL, nil)
	if err != nil {
		return fmt.Errorf("Failed to create new GET Request: %v", err.Error())
	}
	router.ServeHTTP(wv, cv.Request)
	if wv.Code != 200 {

		responseBody, _ := io.ReadAll(wv.Body)

		return fmt.Errorf("VerifyMail: Didn't get the expected response code: got: %v; expected: %v Context: %#v", wv.Code, 200, string(responseBody))
	}
	return nil
}

func getVerificationURL(mailBody string) (string, error) {
	re := regexp.MustCompile(`"([^"]*verify[^"]*)"`)

	// Find the matching URL in the email content
	match := re.FindStringSubmatch(mailBody)
	if len(match) == 0 {
		return "", fmt.Errorf("No mail verification link found in email body: %#v", mailBody)
	}
	verificationURL, err := url.QueryUnescape(match[1])
	if err != nil {
		return "", fmt.Errorf("Error decoding URL: %v", err)
	}
	logger.Info.Printf("VerificationURL: %#v", verificationURL)
	return verificationURL, nil
}

// TEST DATA:
func getBaseUser() models.User {
	return models.User{
		DateOfBirth:    time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
		FirstName:      "John",
		LastName:       "Doe",
		Email:          "john.doe@example.com",
		Address:        "Pablo Escobar Str. 4",
		ZipCode:        "25474",
		City:           "Hasloh",
		Phone:          "01738484993",
		BankAccount:    models.BankAccount{IBAN: "DE89370400440532013000"},
		Membership:     models.Membership{SubscriptionModel: models.SubscriptionModel{Name: "Basic"}},
		ProfilePicture: "",
		Password:       "password123",
		Company:        "",
	}
}

func customizeInput(customize func(models.User) models.User) *RegistrationData {
	user := getBaseUser()
	user = customize(user) // Apply the customization
	return &RegistrationData{User: user}
}

func getTestUsers() []RegisterUserTest {
	return []RegisterUserTest{
		{
			Name:         "birthday < 18 should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.DateOfBirth = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)
				return user
			})),
		},
		{
			Name:         "FirstName empty, should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.FirstName = ""
				return user
			})),
		},
		{
			Name:         "LastName Empty should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.LastName = ""
				return user
			})),
		},
		{
			Name:         "EMail wrong format should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "johnexample.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.Email = "johnexample.com"
				return user
			})),
		},
		{
			Name:         "Missing Zip Code should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.ZipCode = ""
				return user
			})),
		},
		{
			Name:         "Missing Address should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.Address = ""
				return user
			})),
		},
		{
			Name:         "Missing City should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.City = ""
				return user
			})),
		},
		{
			Name:         "Missing IBAN should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.BankAccount.IBAN = ""
				return user
			})),
		},
		{
			Name:         "Invalid IBAN should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.BankAccount.IBAN = "DE1234234123134"
				return user
			})),
		},
		{
			Name:         "Missing subscription plan should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.Membership.SubscriptionModel.Name = ""
				return user
			})),
		},
		{
			Name:         "Invalid subscription plan should fail",
			WantResponse: http.StatusNotFound,
			WantDBData:   map[string]interface{}{"email": "john.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.Membership.SubscriptionModel.Name = "NOTEXISTENTPLAN"
				return user
			})),
		},
		{
			Name:         "Correct Entry should pass",
			WantResponse: http.StatusCreated,
			WantDBData:   map[string]interface{}{"Email": "john.doe@example.com"},
			Assert:       true,
			Input:        GenerateInputJSON(customizeInput(func(user models.User) models.User { return user })),
		},
		{
			Name:         "Email duplicate should fail",
			WantResponse: http.StatusConflict,
			WantDBData:   map[string]interface{}{"first_name": "Jane"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.FirstName = "Jane"
				return user
			})),
		},
		{
			Name:         "Company present should pass",
			WantResponse: http.StatusCreated,
			WantDBData:   map[string]interface{}{"Email": "john.doe2@example.com"},
			Assert:       true,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.Email = "john.doe2@example.com"
				user.Company = "ACME"
				return user
			})),
		},
		{
			Name:         "Subscription constraints not entered; should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"Email": "john.junior.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.Email = "john.junior.doe@example.com"
				user.Membership.SubscriptionModel.Name = "additional"
				return user
			})),
		},
		{
			Name:         "Subscription constraints wrong; should fail",
			WantResponse: http.StatusNotAcceptable,
			WantDBData:   map[string]interface{}{"Email": "john.junior.doe@example.com"},
			Assert:       false,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.Email = "john.junior.doe@example.com"
				user.Membership.ParentMembershipID = 200
				user.Membership.SubscriptionModel.Name = "additional"
				return user
			})),
		},
		{
			Name:         "Subscription constraints correct, should pass",
			WantResponse: http.StatusCreated,
			WantDBData:   map[string]interface{}{"Email": "john.junior.doe@example.com"},
			Assert:       true,
			Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
				user.Email = "john.junior.doe@example.com"
				user.Membership.ParentMembershipID = 1
				user.Membership.SubscriptionModel.Name = "additional"
				return user
			})),
		},
	}
}
