package server

import (
	"errors"
	"fmt"
	"github.com/go-playground/validator/v10"
	"github.com/goccy/go-json"
	"madsky.ru/go-tracker/internal/model/auth"
	"madsky.ru/go-tracker/internal/model/user"
	"madsky.ru/go-tracker/internal/server/helpers"
	"madsky.ru/go-tracker/internal/server/response"
	"net/http"
	"strings"
	"time"
)

const TTL = 12

var validate *validator.Validate

func (app *Application) login(w http.ResponseWriter, r *http.Request) {
	dec := json.NewDecoder(r.Body)
	dec.DisallowUnknownFields()

	var loginDto auth.LoginDTO

	if err := dec.Decode(&loginDto); err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	validate = validator.New(validator.WithRequiredStructEnabled())
	err := validate.Struct(loginDto)
	if err != nil {
		var validateErrs validator.ValidationErrors

		errorsMessages := make([]string, 0)
		if errors.As(err, &validateErrs) {
			for _, e := range validateErrs {
				errorsMessages = append(errorsMessages, fmt.Sprintf("%s is %s", e.Field(), e.Tag()))
			}
		}

		response.Error(w, errors.New(strings.Join(errorsMessages, ",")), http.StatusBadRequest)
		return
	}

	u, err := app.Storage.User.FindByEmail(app.Ctx, loginDto.Email)
	if err != nil {
		response.Error(w, errors.New("user does not exist"), http.StatusBadRequest)
		return
	}

	if !helpers.VerifyHashString(loginDto.Password, u.PasswordHash) {
		response.Error(w, errors.New("wrong password"), http.StatusBadRequest)
		return
	}

	expires := time.Now().Add(time.Hour * TTL)
	token, err := helpers.CreateToken(u.ID, u.Role, expires)
	if err != nil {
		response.Error(w, err, http.StatusInternalServerError)
		return
	}

	data := make(map[string]string)
	data["token"] = token
	data["exp"] = expires.UTC().Format(time.RFC3339)

	cookie := &http.Cookie{
		Name:     "token",
		Path:     "/",
		Value:    token,
		Expires:  expires,
		HttpOnly: true,
	}
	http.SetCookie(w, cookie)

	response.WriteJSON(w, nil, http.StatusCreated, data)
}

func (app *Application) register(w http.ResponseWriter, r *http.Request) {
	dec := json.NewDecoder(r.Body)
	dec.DisallowUnknownFields()

	var registerDto auth.RegisterDTO

	if err := dec.Decode(&registerDto); err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	validate = validator.New(validator.WithRequiredStructEnabled())
	err := validate.Struct(registerDto)
	if err != nil {
		var validateErrs validator.ValidationErrors

		errorsMessages := make([]string, 0)
		if errors.As(err, &validateErrs) {
			for _, e := range validateErrs {
				errorsMessages = append(errorsMessages, fmt.Sprintf("%s is %s", e.Field(), e.Tag()))
			}
		}

		response.Error(w, errors.New(strings.Join(errorsMessages, ",")), http.StatusBadRequest)
		return
	}

	isEmpty, err := app.Storage.User.IsEmpty(app.Ctx)
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	var newUser user.User

	passwordHash, err := helpers.CreateHashString(registerDto.Password)
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	newUser.Email = registerDto.Email
	newUser.Name = registerDto.Name
	newUser.PasswordHash = passwordHash
	if isEmpty {
		newUser.Role = "admin"
	} else {
		newUser.Role = "user"
	}
	res, err := app.Storage.User.Create(app.Ctx, &newUser)
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	response.WriteJSON(w, nil, http.StatusOK, response.ToUserResponseDto(res))

}

func (app *Application) logout(w http.ResponseWriter, _ *http.Request) {
	cookie := &http.Cookie{
		Name:   "token",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	response.WriteJSON(w, nil, http.StatusCreated, 1)
}
