package admin

import (
	"context"
	"errors"

	"git.dmitriygnatenko.ru/dima/go-common/logger"
	validation "github.com/go-ozzo/ozzo-validation/v4"
	"github.com/gofiber/fiber/v2"

	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/dto"
	customErrors "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/helper/errors"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/mapper"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/model"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/service/auth"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/service/i18n"
)

type changePasswordPage struct {
	authService    AuthService
	userRepository UserRepository
}

func NewChangePasswordPageHandler(
	authService AuthService,
	userRepository UserRepository,
) fiber.Handler {
	h := changePasswordPage{
		authService:    authService,
		userRepository: userRepository,
	}

	return h.handler()
}

func (h changePasswordPage) handler() fiber.Handler {
	return func(fctx *fiber.Ctx) error {
		ctx := fctx.Context()
		lang := i18n.LanguageFromContext(fctx)
		validationErrors := make(map[string]string)

		var form dto.ChangePasswordForm

		if fctx.Method() == fiber.MethodPost {
			if err := fctx.BodyParser(&form); err != nil {
				logger.Info(ctx, customErrors.Wrap(err, "change user password page: can't parse body").Error())
				return err
			}

			claims := h.authService.GetClaims(fctx)

			user, err := h.userRepository.Get(ctx, claims[auth.ClaimNameKey].(string))
			if err != nil {
				logger.Error(ctx, customErrors.Wrap(err, "change user password page: can't get user").Error())
				return err
			}

			if err := h.validateChangePasswordForm(ctx, form, *user); err != nil {
				logger.Info(ctx, customErrors.Wrap(err, "change user password page: validation").Error())
				validationErrors = mapper.ToErrorsMap(err)
			}

			if len(validationErrors) == 0 {
				newPassword, err := h.authService.GeneratePasswordHash(form.NewPassword)
				if err != nil {
					logger.Error(
						ctx,
						customErrors.Wrap(err, "change user password page: can't generate new password").Error(),
					)
					return err
				}

				if err = h.userRepository.UpdatePassword(ctx, user.ID, newPassword); err != nil {
					logger.Error(
						ctx,
						customErrors.Wrap(err, "change user password page: can't update user").Error(),
					)
					return err
				}

				return fctx.Redirect("/admin")
			}
		}

		return fctx.Render("admin/user_change_password", fiber.Map{
			"lang":    lang,
			"section": "change_password",
			"form":    form,
			"errors":  validationErrors,
		}, "admin/_layout")
	}
}

func (h changePasswordPage) validateChangePasswordForm(
	ctx context.Context,
	req dto.ChangePasswordForm,
	user model.User,
) error {
	lang := i18n.DefaultLanguage()
	requiredMsg := i18n.T(lang, "admin_err_required")
	lengthMsg := i18n.T(lang, "admin_err_length_255")

	return validation.ValidateStructWithContext(
		ctx,
		&req,
		validation.Field(
			&req.OldPassword,
			validation.Required.Error(requiredMsg),
			validation.Length(1, 255).Error(lengthMsg),
			validation.By(h.validateOldPassword(user)),
		),
		validation.Field(
			&req.NewPassword,
			validation.Required.Error(requiredMsg),
			validation.Length(1, 255).Error(lengthMsg),
		),
	)
}

func (h changePasswordPage) validateOldPassword(user model.User) func(value interface{}) error {
	return func(value interface{}) error {
		oldPassword, _ := value.(string)

		if !h.authService.IsCorrectPassword(oldPassword, user.Password) {
			lang := i18n.DefaultLanguage()
			return errors.New(i18n.T(lang, "admin_err_incorrect_password"))
		}

		return nil
	}
}
