package api

import (
	"errors"
	"net/http"
	"strconv"

	"party.at/party/cmd/party/common"
	"party.at/party/internal/data"
	"party.at/party/internal/validator"
)

func (api *Api) ListUsers(w http.ResponseWriter, r *http.Request) {
	var input struct {
		data.Filters
	}

	v := validator.New()
	qs := r.URL.Query()

	input.Filters.Page = common.ReadInt(qs, "page", 1, v)
	input.Filters.PageSize = common.ReadInt(qs, "page_size", 20, v)
	input.Filters.Sort = common.ReadString(qs, "sort", "id")
	input.Filters.SortSafelist = []string{"id", "-id", "name", "-name", "email", "-email", "created", "-created"}

	if data.ValidateFilters(v, input.Filters); !v.Valid() {
		api.errorResponse(w, r, data.ErrValidationFailed.(*data.Error).WithDetails(v.Errors))
		return
	}

	users, metadata, err := api.App.ListUsers(input.Filters)
	if err != nil {
		api.ServerErrorResponse(w, r, err)
		return
	}

	if err = common.WriteJSON(w, http.StatusOK, common.Envelope{"users": users, "metadata": metadata}, nil); err != nil {
		api.ServerErrorResponse(w, r, err)
	}
}

func (api *Api) CreateUser(w http.ResponseWriter, r *http.Request) {
	var input common.RegisterUserInput

	if err := common.ReadJSON(w, r, &input); err != nil {
		api.errorResponse(w, r, data.ErrBadRequest)
		return
	}

	user, authToken, err := api.App.RegisterUser(input)
	if err != nil {
		api.errorResponse(w, r, err)
		return
	}

	if err = common.WriteJSON(w, http.StatusCreated, common.Envelope{"user": user, "authentication_token": authToken}, nil); err != nil {
		api.ServerErrorResponse(w, r, err)
	}
}

func (api *Api) ReadUser(w http.ResponseWriter, r *http.Request) {
	user := common.GetUser(r)

	param := r.PathValue("id")
	
	var id int64
	if param == "me" {
		id = common.GetUser(r).ID
	} else {
		var err error
		id, err = strconv.ParseInt(param, 10, 64)
		if err != nil || id < 1 {
			api.errorResponse(w, r, data.ErrRecordNotFound)
			return
		}
	}

	target, err := api.App.GetUser(user, id)
	if err != nil {
		if errors.Is(err, data.ErrRecordNotFound) {
			api.errorResponse(w, r, data.ErrRecordNotFound)
		} else {
			api.ServerErrorResponse(w, r, err)
		}
		return
	}

	if err = common.WriteJSON(w, http.StatusOK, common.Envelope{"user": target}, nil); err != nil {
		api.ServerErrorResponse(w, r, err)
	}
}

// func (api *Api) ReadAuthenticatedUser(w http.ResponseWriter, r *http.Request) {
// 	user := common.GetUser(r).ID

// 	if err := common.WriteJSON(w, http.StatusOK, common.Envelope{"user": user}, nil); err != nil {
// 		api.ServerErrorResponse(w, r, err)
// 	}
// }

func (api *Api) DeleteUser(w http.ResponseWriter, r *http.Request) {
	id, err := common.ReadIDParam(r)
	if err != nil {
		api.errorResponse(w, r, data.ErrRecordNotFound)
		return
	}

	if err = api.App.DeleteUser(id); err != nil {
		if errors.Is(err, data.ErrRecordNotFound) {
			api.errorResponse(w, r, data.ErrRecordNotFound)
		} else {
			api.ServerErrorResponse(w, r, err)
		}
		return
	}

	if err = common.WriteJSON(w, http.StatusOK, common.Envelope{"message": "user successfully deleted"}, nil); err != nil {
		api.ServerErrorResponse(w, r, err)
	}
}

func (api *Api) ActivateUser(w http.ResponseWriter, r *http.Request) {
	var input struct {
		TokenPlaintext string `json:"token"`
	}

	if err := common.ReadJSON(w, r, &input); err != nil {
		api.errorResponse(w, r, data.ErrBadRequest)
		return
	}

	v := validator.New()
	if data.ValidateTokenPlaintext(v, input.TokenPlaintext); !v.Valid() {
		api.errorResponse(w, r, data.ErrValidationFailed.(*data.Error).WithDetails(v.Errors))
		return
	}

	user, err := api.App.ActivateUser(input.TokenPlaintext)
	if err != nil {
		api.errorResponse(w, r, err)
		return
	}

	if err = common.WriteJSON(w, http.StatusOK, common.Envelope{"user": user}, nil); err != nil {
		api.ServerErrorResponse(w, r, err)
	}
}
