package account

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/account"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
	"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/echotools"
)

type Deps struct {
	Logger  *zap.Logger
	Service *account.Service
}

type Controller struct {
	logger  *zap.Logger
	service *account.Service
}

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

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

	return &Controller{
		logger:  deps.Logger,
		service: deps.Service,
	}
}

func (receiver *Controller) GetAccount(ctx echo.Context) error {
	userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
	if !ok {
		receiver.logger.Error("failed to convert jwt payload to string on <GetAccount> of <AccountController>")

		return errors.HTTP(ctx, errors.New(
			fmt.Errorf("failed to convert jwt payload to string: %s", userID),
			errors.ErrInvalidArgs,
		))
	}

	account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userID)
	if err != nil {
		return errors.HTTP(ctx, err)
	}

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

func (receiver *Controller) GetDirectAccount(ctx echo.Context, userID string) error {
	account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userID)
	if err != nil {
		return errors.HTTP(ctx, err)
	}

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

func (receiver *Controller) GetAccounts(ctx echo.Context, params swagger.PaginationAccountsParams) error {
	response, err := receiver.service.GetAccountsList(
		ctx.Request().Context(),
		utils.DeterminePagination(params.Page, params.Limit),
	)
	if err != nil {
		return errors.HTTP(ctx, err)
	}

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

func (receiver *Controller) CreateAccount(ctx echo.Context) error {
	userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
	if !ok {
		receiver.logger.Error("failed to convert jwt payload to string on <CreateAccount> of <AccountController>")

		return errors.HTTP(ctx, errors.New(
			fmt.Errorf("failed to convert jwt payload to string: %s", userID),
			errors.ErrInvalidArgs,
		))
	}

	account, err := receiver.service.CreateAccountByUserID(ctx.Request().Context(), userID)
	if err != nil {
		return errors.HTTP(ctx, err)
	}

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

func (receiver *Controller) RemoveAccount(ctx echo.Context) error {
	userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
	if !ok {
		receiver.logger.Error("failed to convert jwt payload to string on <RemoveAccount> of <AccountController>")

		return errors.HTTP(ctx, errors.New(
			fmt.Errorf("failed to convert jwt payload to string: %s", userID),
			errors.ErrInvalidArgs,
		))
	}

	account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userID)
	if err != nil {
		return errors.HTTP(ctx, err)
	}

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

func (receiver *Controller) RemoveDirectAccount(ctx echo.Context, userID string) error {
	account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userID)
	if err != nil {
		return errors.HTTP(ctx, err)
	}

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

func (receiver *Controller) SetVerificationStatus(ctx echo.Context, userID string) error {
	request, bindErr := echotools.Bind[models.SetAccountStatus](ctx)
	if bindErr != nil {
		receiver.logger.Error("failed to bind json request on <SetVerificationStatus> of <AccountController>", zap.Error(bindErr))
		return errors.HTTP(ctx, errors.New(fmt.Errorf("failed to bind json: %w", bindErr), errors.ErrInternalError))
	}

	account, err := receiver.service.SetVerificationStatus(ctx.Request().Context(), userID, request.Status)
	if err != nil {
		receiver.logger.Error("failed set status on <SetVerificationStatus> of <AccountController>", zap.Error(err))
		return errors.HTTP(ctx, err)
	}

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

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

	request, bindErr := echotools.Bind[models.Name](ctx)
	if bindErr != nil {
		receiver.logger.Error("failed to bind json request on <UpdateAccountName> of <AccountController>", zap.Error(bindErr))
		return errors.HTTP(ctx, errors.New(fmt.Errorf("failed to bind json: %w", bindErr), errors.ErrInternalError))
	}

	account, err := receiver.service.UpdateAccountName(ctx.Request().Context(), userID, request)
	if err != nil {
		receiver.logger.Error("failed to update account name on <UpdateAccountName> of <AccountController>", zap.Error(err))
		return errors.HTTP(ctx, err)
	}

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