package cart

import (
	"fmt"
	"log"
	"net/http"

	"github.com/labstack/echo/v4"
	"go.uber.org/zap"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/cart"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
)

type Deps struct {
	Logger      *zap.Logger
	CartService *cart.Service
}

type Controller struct {
	logger      *zap.Logger
	cartService *cart.Service
}

func New(deps Deps) *Controller {
	if deps.Logger == nil {
		log.Panicln("logger is nil on <New (cart controller)>")
	}

	if deps.CartService == nil {
		log.Panicln("cart service is nil on <New (cart controller)>")
	}

	return &Controller{
		logger:      deps.Logger,
		cartService: deps.CartService,
	}
}

func (receiver *Controller) Remove(ctx echo.Context, params swagger.RemoveFromCartParams) error {
	userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
	if !ok {
		receiver.logger.Error("failed to convert jwt payload to string on <Remove> of <CartController>")
		return errors.HTTP(ctx, errors.NewWithMessage("failed to convert jwt payload to string", errors.ErrInvalidArgs))
	}

	if validate.IsStringEmpty(params.Id) {
		return errors.HTTP(ctx, errors.New(
			fmt.Errorf("failed to remove cart item from user <%s>: empty item id", userID),
			errors.ErrInvalidArgs,
		))
	}

	cartItems, err := receiver.cartService.Remove(ctx.Request().Context(), userID, params.Id)
	if err != nil {
		receiver.logger.Error("failed to remove item from cart on <Remove> of <CartController>", zap.Error(err))
		return errors.HTTP(ctx, err)
	}

	return ctx.JSON(http.StatusOK, cartItems)
}

func (receiver *Controller) Add(ctx echo.Context, params swagger.Add2cartParams) error {
	userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
	if !ok {
		receiver.logger.Error("failed to convert jwt payload to string on <Add> of <CartController>")
		return errors.HTTP(ctx, errors.NewWithMessage("failed to convert jwt payload to string", errors.ErrInvalidArgs))
	}

	token, ok := ctx.Get(models.AuthJWTDecodedAccessTokenKey).(string)
	if !ok {
		receiver.logger.Error("failed to convert access token payload to string on <Add> of <CartController>")
		return errors.HTTP(ctx, errors.NewWithMessage("failed to convert access token payload to string", errors.ErrInvalidArgs))
	}

	if validate.IsStringEmpty(params.Id) {
		return errors.HTTP(ctx, errors.New(
			fmt.Errorf("failed to add cart item to user <%s>: empty item id", userID),
			errors.ErrInvalidArgs,
		))
	}

	cartItems, err := receiver.cartService.Add(ctx.Request().Context(), &models.AddItemToCart{
		UserID:      userID,
		TariffID:    params.Id,
		AccessToken: token,
	})
	if err != nil {
		receiver.logger.Error("failed to add item to cart on <Add> of <CartController>", zap.Error(err))
		return errors.HTTP(ctx, err)
	}

	return ctx.JSON(http.StatusOK, cartItems)
}

func (receiver *Controller) Pay(ctx echo.Context) error {
	userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
	if !ok {
		receiver.logger.Error("failed to convert jwt payload to string on <Pay> of <CartController>")
		return errors.HTTP(ctx, errors.NewWithMessage("failed to convert jwt payload to string", errors.ErrInvalidArgs))
	}

	token, ok := ctx.Get(models.AuthJWTDecodedAccessTokenKey).(string)
	if !ok {
		receiver.logger.Error("failed to convert access token payload to string on <Pay> of <CartController>")
		return errors.HTTP(ctx, errors.NewWithMessage("failed to convert access token payload to string", errors.ErrInvalidArgs))
	}

	account, err := receiver.cartService.Pay(ctx.Request().Context(), token, userID)
	if err != nil {
		receiver.logger.Error("failed to pay cart on <Pay> of <CartController>", zap.Error(err))
		return errors.HTTP(ctx, err)
	}

	return ctx.JSON(http.StatusOK, account)
}
