package server

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

func (app *Application) findProjects(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	u := ctx.Value(UserContext{}).(*user.User)

	var filter project.FilterDTO

	if u.Role != "admin" {
		filter.UserID = &u.ID
	}

	isAdmin := u.Role == "admin"

	p, err := app.Storage.Projects.Find(app.Ctx, u.ID, isAdmin, &filter)
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	response.WriteJSON(w, nil, http.StatusOK, &p)
}

func (app *Application) findProjectByID(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	u := ctx.Value(UserContext{}).(*user.User)

	projectId, err := request.Param(r, "id")
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	var filter project.FilterDTO

	if u.Role != "admin" {
		filter.UserID = &u.ID
	}

	isAdmin := u.Role == "admin"

	p, err := app.Storage.Projects.FindOne(app.Ctx, projectId, u.ID, isAdmin)
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}
	response.WriteJSON(w, nil, http.StatusOK, &p)
}

func (app *Application) createProject(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	u := ctx.Value(UserContext{}).(*user.User)

	dec := json.NewDecoder(r.Body)
	dec.DisallowUnknownFields()

	var projectDto project.CreateProjectDTO

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

	validate = validator.New(validator.WithRequiredStructEnabled())
	err := validate.Struct(projectDto)
	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
	}

	p, err := app.Storage.Projects.Create(app.Ctx, &projectDto)
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	err = app.Storage.UserToProject.Create(app.Ctx, u.ID, p.ID)
	if err != nil {
		response.Error(w, err, http.StatusInternalServerError)
	}

	response.WriteJSON(w, nil, http.StatusCreated, &p)
}

func (app *Application) updateProject(w http.ResponseWriter, r *http.Request) {
	id, err := request.Param(r, "id")
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	dec := json.NewDecoder(r.Body)
	dec.DisallowUnknownFields()

	var projectDto project.UpdateProjectDTO

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

	p, err := app.Storage.Projects.Update(app.Ctx, id, &projectDto)
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	response.WriteJSON(w, nil, http.StatusOK, &p)
}

func (app *Application) deleteProject(w http.ResponseWriter, r *http.Request) {
	id, err := request.Param(r, "id")
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	count, err := app.Storage.Projects.Remove(app.Ctx, id)
	if err != nil {
		response.Error(w, err, http.StatusBadRequest)
		return
	}

	response.WriteJSON(w, nil, http.StatusOK, count)
}
