package admin

import (
	"context"
	"time"

	"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/model"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/service/i18n"
)

type loginPage struct {
	configService  ConfigService
	authService    AuthService
	userRepository UserRepository
}

func NewLoginPageHandler(
	configService ConfigService,
	authService AuthService,
	userRepository UserRepository,
) fiber.Handler {
	handler := loginPage{
		configService:  configService,
		authService:    authService,
		userRepository: userRepository,
	}

	return handler.handler()
}

func (h loginPage) handler() fiber.Handler {
	return func(fctx *fiber.Ctx) error {
		ctx := fctx.Context()
		lang := i18n.LanguageFromContext(fctx)
		var hasErrors bool

		form := dto.LoginForm{}

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

			if err := validateLoginForm(ctx, form); err != nil {
				logger.Info(ctx, customErrors.Wrap(err, "login page: validation").Error())
				hasErrors = true
			}

			if !hasErrors {
				user, err := h.userRepository.Get(ctx, form.Username)
				if err != nil {
					if !db.IsNotFoundError(err) {
						logger.Error(ctx, customErrors.Wrap(err, "login page: can't get user").Error())
						return err
					}
					hasErrors = true
				}

				if !hasErrors {
					if h.authService.IsCorrectPassword(form.Password, user.Password) {
						cookie, cookieErr := h.createCookie(*user)
						if cookieErr != nil {
							logger.Error(ctx, customErrors.Wrap(err, "login page: can't create cookie").Error())
							return cookieErr
						}

						fctx.Cookie(cookie)

						return fctx.Redirect("/admin")
					}
					hasErrors = true
				}
			}
		}

		return fctx.Render("admin/login", fiber.Map{
			"lang":      lang,
			"form":      form,
			"hasErrors": hasErrors,
		})
	}
}

func (h loginPage) createCookie(user model.User) (*fiber.Cookie, error) {
	token, err := h.authService.GenerateToken(user)
	if err != nil {
		return nil, customErrors.Wrap(err, "can't generate token")
	}

	cookie := new(fiber.Cookie)
	cookie.Name = h.configService.JWTCookie()
	cookie.Value = token
	cookie.Expires = time.Now().Add(h.configService.JWTLifeTime())

	return cookie, nil
}

func validateLoginForm(ctx context.Context, req dto.LoginForm) error {
	return validation.ValidateStructWithContext(
		ctx,
		&req,
		validation.Field(
			&req.Username,
			validation.Required,
		),
		validation.Field(
			&req.Password,
			validation.Required,
		),
	)
}
