/*
 * OpenAPI Petstore
 *
 * This is a sample of Petstore application. For this sample, you can use the api key `special-key` to test the authorization filters.
 *
 * API version: 1.0.0
 * Generated by: OpenAPI Generator (https://openapi-generator.tech)
 */

package openapi

import (
	"github.com/gin-gonic/gin"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type UserAPI struct {
	Users map[string]User
}

func (api *UserAPI) InitializeUsersMap() {
	if api.Users == nil {
		api.Users = make(map[string]User)
	}
}

// Post /v2/user/createInQueryMap
// Testing map query operation parameter 
func (api *UserAPI) CreateInQueryMap(c *gin.Context) {
	api.InitializeUsersMap()

	m, err := url.ParseQuery(c.Request.URL.RawQuery)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse query"})
		return
	}

	// The map should contain Username as a key, and UserStatus as a single
	// element of a value array, e.g. map[Ivan:[1804289383]]
	var requestUser User
	for k, v := range m {
		requestUser.Username = k
		i64, _ := strconv.ParseInt(v[0], 10, 32)
		requestUser.UserStatus = int32(i64)
		api.Users[k] = requestUser
	}

	c.JSON(http.StatusOK, gin.H{"status": "OK"})
}

// Post /v2/user
// Create user 
func (api *UserAPI) CreateUser(c *gin.Context) {
	api.InitializeUsersMap()

	var requestUser User
	if err := c.ShouldBindJSON(&requestUser); err != nil {
		fmt.Println("CreateUser: failed to parse user", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	api.Users[requestUser.Username] = requestUser

	c.JSON(http.StatusOK, gin.H{"status": "OK"})
}

// Post /v2/user/createWithArray
// Creates list of users with given input array 
func (api *UserAPI) CreateUsersWithArrayInput(c *gin.Context) {
	api.InitializeUsersMap()

	var requestUsers []User
	if err := c.ShouldBindJSON(&requestUsers); err != nil {
		fmt.Println("CreateUsersWithArrayInput: failed to parse users", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	for _, usr := range requestUsers {
		api.Users[usr.Username] = usr
	}

	c.JSON(http.StatusOK, gin.H{"status": "OK"})
}

// Post /v2/user/createWithList
// Creates list of users with given input array 
func (api *UserAPI) CreateUsersWithListInput(c *gin.Context) {
	api.CreateUsersWithArrayInput(c)
}

// Helper function to parse string key and a value into a proper field
// of the User struct
func (api *UserAPI) ParseEntryToField(key string, value string, user *User) bool {
	var result bool = false

	switch key {
	case "id":
		var err error = nil
		user.Id, err = strconv.ParseInt(value, 10, 64)
		result = err == nil
	case "username":
		user.Username = value
		result = true
	case "firstName":
		user.FirstName = value
		result = true
	case "lastName":
		user.LastName = value
		result = true
	case "email":
		user.Email = value
		result = true
	case "password":
		user.Password = value
		result = true
	case "phone":
		user.Phone = value
		result = true
	case "userStatus":
		i64, err := strconv.ParseInt(value, 10, 32)
		result = err == nil
		if result {
			user.UserStatus = int32(i64)
		}
	}
	return result
}

func (api *UserAPI) PathParamToUser(userStr string, user *User) bool {
	vals := strings.Split(userStr, ",")
	total := len(vals)
	if total % 2 != 0 {
		return false
	}
	for i := 0; i < total; i = i + 2 {
		if !api.ParseEntryToField(vals[i], vals[i + 1], user) {
			return false
		}
	}
	return true
}

// Delete /v2/user/:username
// Delete user 
func (api *UserAPI) DeleteUser(c *gin.Context) {
	api.InitializeUsersMap()

	userStr := c.Param("username")
	if userStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "missing username"})
		return
	}

	// userStr is a full user description here, e.g.
	// "email,Jane.Doe@QtOpenAPItools.io,firstName,Jane,id,1759761133409,lastName,Doe,
	// password,secretPassword,phone,123456789,userStatus,596516649,username,sylvester"
	var requestUser User
	if !api.PathParamToUser(userStr, &requestUser) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse user"})
		return
	}

	usr, ok := api.Users[requestUser.Username]

	if !ok || usr != requestUser {
		c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
		return
	}
	delete(api.Users, requestUser.Username)

	c.JSON(http.StatusOK, gin.H{"status": "OK"})
}

// Get /v2/user/:username
// Get user by user name 
func (api *UserAPI) GetUserByName(c *gin.Context) {
	api.InitializeUsersMap()

	userStr := c.Param("username")
	if userStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "missing username"})
		return
	}

	var requestUser User
	// The full request looks like /v2/user/Ivan,1804289383
	// userStr contains name and status, e.g. Ivan,1804289383

	vals := strings.Split(userStr, ",")
	if len(vals) != 2 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse user"})
		return
	}

	requestUser.Username = vals[0]
	i64, err := strconv.ParseInt(vals[1], 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse user"})
		return
	}
	requestUser.UserStatus = int32(i64)

	usr, ok := api.Users[requestUser.Username]

	if !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
		return
	}

	c.JSON(http.StatusOK, usr)
}

// Get /v2/user/login
// Logs user into the system 
func (api *UserAPI) LoginUser(c *gin.Context) {
	m, err := url.ParseQuery(c.Request.URL.RawQuery)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse query"})
		return
	}

	usr, ok := m["username"] // returns a list!
	var total string
	if ok && len(usr) > 0 {
		total = usr[0]
	}
	pwd, ok := m["password"] // returns a list!
	if ok && len(pwd) > 0 {
		total += pwd[0]
	}

	// yaml file tells to return a JSON, but the test expects a string!
	// c.JSON(http.StatusOK, total)
	c.String(http.StatusOK, total)
}

// Get /v2/user/logout
// Logs out current logged in user session 
func (api *UserAPI) LogoutUser(c *gin.Context) {
	// The test does nothing! Same was the C++ version!
	c.JSON(http.StatusOK, gin.H{"status": "OK"})
}

// Put /v2/user/:username
// Updated user 
func (api *UserAPI) UpdateUser(c *gin.Context) {
	api.InitializeUsersMap()

	userStr := c.Param("username")
	if userStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "missing username"})
		return
	}

	_, ok := api.Users[userStr]
	if !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
		return
	}

	m, err := url.ParseQuery(c.Request.URL.RawQuery)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse query"})
		return
	}

	// Fill user from query parameters
	// map contains the full user description, e.g.:
	// map[email:[Jane.Doe@QtOpenAPItools.io] firstName:[Stephan] id:[1759761423083]
	//     lastName:[Newman] password:[secretPassword] phone:[123456789]
	//     userStatus:[1350490027] username:[MrGrump]]
	var user User
	for k, v := range m {
		val := v[0] // v is a list!
		if !api.ParseEntryToField(k, val, &user) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "failed to parse user"})
			return
		}
	}

	api.Users[userStr] = user

	c.JSON(http.StatusOK, user)
}

