package handler

import (
	"strconv"

	"apskel-pos-be/internal/appcontext"
	"apskel-pos-be/internal/constants"
	"apskel-pos-be/internal/contract"
	"apskel-pos-be/internal/logger"
	"apskel-pos-be/internal/service"
	"apskel-pos-be/internal/util"
	"apskel-pos-be/internal/validator"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type PaymentMethodHandler struct {
	paymentMethodService   service.PaymentMethodService
	paymentMethodValidator validator.PaymentMethodValidator
}

func NewPaymentMethodHandler(
	paymentMethodService service.PaymentMethodService,
	paymentMethodValidator validator.PaymentMethodValidator,
) *PaymentMethodHandler {
	return &PaymentMethodHandler{
		paymentMethodService:   paymentMethodService,
		paymentMethodValidator: paymentMethodValidator,
	}
}

func (h *PaymentMethodHandler) CreatePaymentMethod(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	var req contract.CreatePaymentMethodRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		logger.FromContext(c.Request.Context()).WithError(err).Error("PaymentMethodHandler::CreatePaymentMethod -> request binding failed")
		validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::CreatePaymentMethod")
		return
	}

	validationError, validationErrorCode := h.paymentMethodValidator.ValidateCreatePaymentMethodRequest(&req)
	if validationError != nil {
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::CreatePaymentMethod")
		return
	}

	req.OrganizationID = contextInfo.OrganizationID
	req.OutletID = contextInfo.OutletID

	paymentMethodResponse := h.paymentMethodService.CreatePaymentMethod(ctx, contextInfo, &req)
	if paymentMethodResponse.HasErrors() {
		errorResp := paymentMethodResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("PaymentMethodHandler::CreatePaymentMethod -> Failed to create payment method from service")
	}

	util.HandleResponse(c.Writer, c.Request, paymentMethodResponse, "PaymentMethodHandler::CreatePaymentMethod")
}

func (h *PaymentMethodHandler) GetPaymentMethod(c *gin.Context) {
	ctx := c.Request.Context()

	paymentMethodIDStr := c.Param("id")
	paymentMethodID, err := uuid.Parse(paymentMethodIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("PaymentMethodHandler::GetPaymentMethod -> Invalid payment method ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid payment method ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::GetPaymentMethod")
		return
	}

	paymentMethodResponse := h.paymentMethodService.GetPaymentMethodByID(ctx, paymentMethodID)
	if paymentMethodResponse.HasErrors() {
		errorResp := paymentMethodResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("PaymentMethodHandler::GetPaymentMethod -> Failed to get payment method from service")
	}

	util.HandleResponse(c.Writer, c.Request, paymentMethodResponse, "PaymentMethodHandler::GetPaymentMethod")
}

func (h *PaymentMethodHandler) ListPaymentMethods(c *gin.Context) {
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	req := &contract.ListPaymentMethodsRequest{
		Page:  1,
		Limit: 10,
	}

	if pageStr := c.Query("page"); pageStr != "" {
		if page, err := strconv.Atoi(pageStr); err == nil {
			req.Page = page
		}
	}

	if limitStr := c.Query("limit"); limitStr != "" {
		if limit, err := strconv.Atoi(limitStr); err == nil {
			req.Limit = limit
		}
	}

	if search := c.Query("search"); search != "" {
		req.Search = search
	}

	if paymentMethodType := c.Query("type"); paymentMethodType != "" {
		req.Type = &paymentMethodType
	}

	req.OrganizationID = &contextInfo.OrganizationID

	if isActiveStr := c.Query("is_active"); isActiveStr != "" {
		if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
			req.IsActive = &isActive
		}
	}

	validationError, validationErrorCode := h.paymentMethodValidator.ValidateListPaymentMethodsRequest(req)
	if validationError != nil {
		logger.FromContext(ctx).WithError(validationError).Error("PaymentMethodHandler::ListPaymentMethods -> request validation failed")
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::ListPaymentMethods")
		return
	}

	paymentMethodsResponse := h.paymentMethodService.ListPaymentMethods(ctx, req)
	if paymentMethodsResponse.HasErrors() {
		errorResp := paymentMethodsResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("PaymentMethodHandler::ListPaymentMethods -> Failed to list payment methods from service")
	}

	util.HandleResponse(c.Writer, c.Request, paymentMethodsResponse, "PaymentMethodHandler::ListPaymentMethods")
}

func (h *PaymentMethodHandler) UpdatePaymentMethod(c *gin.Context) {
	ctx := c.Request.Context()

	paymentMethodIDStr := c.Param("id")
	paymentMethodID, err := uuid.Parse(paymentMethodIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("PaymentMethodHandler::UpdatePaymentMethod -> Invalid payment method ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid payment method ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::UpdatePaymentMethod")
		return
	}

	var req contract.UpdatePaymentMethodRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		logger.FromContext(ctx).WithError(err).Error("PaymentMethodHandler::UpdatePaymentMethod -> request binding failed")
		validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::UpdatePaymentMethod")
		return
	}

	validationError, validationErrorCode := h.paymentMethodValidator.ValidateUpdatePaymentMethodRequest(&req)
	if validationError != nil {
		validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::UpdatePaymentMethod")
		return
	}

	paymentMethodResponse := h.paymentMethodService.UpdatePaymentMethod(ctx, paymentMethodID, &req)
	if paymentMethodResponse.HasErrors() {
		errorResp := paymentMethodResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("PaymentMethodHandler::UpdatePaymentMethod -> Failed to update payment method from service")
	}

	util.HandleResponse(c.Writer, c.Request, paymentMethodResponse, "PaymentMethodHandler::UpdatePaymentMethod")
}

func (h *PaymentMethodHandler) DeletePaymentMethod(c *gin.Context) {
	ctx := c.Request.Context()

	paymentMethodIDStr := c.Param("id")
	paymentMethodID, err := uuid.Parse(paymentMethodIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("PaymentMethodHandler::DeletePaymentMethod -> Invalid payment method ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid payment method ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::DeletePaymentMethod")
		return
	}

	paymentMethodResponse := h.paymentMethodService.DeletePaymentMethod(ctx, paymentMethodID)
	if paymentMethodResponse.HasErrors() {
		errorResp := paymentMethodResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("PaymentMethodHandler::DeletePaymentMethod -> Failed to delete payment method from service")
	}

	util.HandleResponse(c.Writer, c.Request, paymentMethodResponse, "PaymentMethodHandler::DeletePaymentMethod")
}

func (h *PaymentMethodHandler) GetActivePaymentMethodsByOrganization(c *gin.Context) {
	ctx := c.Request.Context()

	organizationIDStr := c.Param("organization_id")
	organizationID, err := uuid.Parse(organizationIDStr)
	if err != nil {
		logger.FromContext(ctx).WithError(err).Error("PaymentMethodHandler::GetActivePaymentMethodsByOrganization -> Invalid organization ID")
		validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid organization ID")
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PaymentMethodHandler::GetActivePaymentMethodsByOrganization")
		return
	}

	paymentMethodsResponse := h.paymentMethodService.GetActivePaymentMethodsByOrganization(ctx, organizationID)
	if paymentMethodsResponse.HasErrors() {
		errorResp := paymentMethodsResponse.GetErrors()[0]
		logger.FromContext(ctx).WithError(errorResp).Error("PaymentMethodHandler::GetActivePaymentMethodsByOrganization -> Failed to get active payment methods from service")
	}

	util.HandleResponse(c.Writer, c.Request, paymentMethodsResponse, "PaymentMethodHandler::GetActivePaymentMethodsByOrganization")
}
