package admin

import (
	"context"
	"errors"

	"git.dmitriygnatenko.ru/dima/go-common/db"
	"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/helper/request"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/mapper"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/service/handler"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/service/i18n"
)

type editTagPage struct {
	tagRepository      TagRepository
	languageRepository LanguageRepository
	cacheService       CacheService
}

func NewEditTagPageHandler(
	tagRepository TagRepository,
	languageRepository LanguageRepository,
	cacheService CacheService,
) fiber.Handler {
	h := editTagPage{
		tagRepository:      tagRepository,
		languageRepository: languageRepository,
		cacheService:       cacheService,
	}

	return h.handler()
}

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

		tagID, err := request.ConvertToUint64(fctx, "id")
		if err != nil {
			logger.Error(ctx, customErrors.Wrap(err, "edit tag page: can't get id from request").Error())
			return err
		}

		tag, err := h.tagRepository.GetByID(ctx, tagID)
		if err != nil {
			logger.Error(ctx, customErrors.Wrap(err, "edit tag page: can't get tag").Error())
			return err
		}

		form := dto.TagForm{
			ID:  tagID,
			Tag: tag.Tag,
			URL: tag.URL,
		}

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

			if err := h.validateTagForm(ctx, form, tag.URL); err != nil {
				logger.Info(ctx, customErrors.Wrap(err, "edit tag page: validation").Error())
				validationErrors = mapper.ToErrorsMap(err)
			}

			if len(validationErrors) == 0 {
				err = h.tagRepository.Update(ctx, mapper.ToTagModel(form))
				if err != nil {
					logger.Error(ctx, customErrors.Wrap(err, "edit tag page: can't update tag").Error())
					return err
				}

				languages, err := h.languageRepository.GetAll(ctx)
				if err != nil {
					logger.Error(ctx, customErrors.Wrap(err, "edit tag page: can't get languages").Error())
					return err
				}

				for i := range languages {
					h.cacheService.Delete(handler.TagCacheKey + languages[i].URL + tag.URL)
					h.cacheService.Delete(handler.UsedTagsCacheKey + languages[i].URL)
				}

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

		return fctx.Render("admin/tag_edit", fiber.Map{
			"lang":    lang,
			"section": "tag",
			"form":    form,
			"errors":  validationErrors,
			"title":   i18n.T(lang, "admin_tag_edit_title"),
		}, "admin/_layout")
	}
}

func (h editTagPage) validateTagForm(
	ctx context.Context,
	req dto.TagForm,
	allowedURL string,
) 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.Tag,
			validation.Required.Error(requiredMsg),
			validation.Length(1, 255).Error(lengthMsg),
		),
		validation.Field(
			&req.URL,
			validation.Required.Error(requiredMsg),
			validation.Length(1, 255).Error(lengthMsg),
			validation.By(h.validateDuplicateURL(allowedURL)),
		),
	)
}

func (h editTagPage) validateDuplicateURL(allowedURL string) func(value interface{}) error {
	return func(value interface{}) error {
		url, _ := value.(string)

		if url == allowedURL {
			return nil
		}

		ctx := context.Background()
		lang := i18n.DefaultLanguage()

		_, err := h.tagRepository.GetByURL(ctx, url)
		if err != nil {
			if !db.IsNotFoundError(err) {
				logger.Error(ctx, err.Error())
			}

			return nil
		}

		return errors.New(i18n.T(lang, "admin_err_tag_duplicate", url))
	}
}
