package handler

import (
	"apskel-pos-be/internal/appcontext"
	"apskel-pos-be/internal/contract"
	"apskel-pos-be/internal/service"
	"apskel-pos-be/internal/transformer"
	"apskel-pos-be/internal/util"
	"apskel-pos-be/internal/validator"

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

type OrderHandler struct {
	orderService service.OrderService
	validator    validator.OrderValidator
	transformer  transformer.Transformer
}

func NewOrderHandler(
	orderService service.OrderService,
	validator validator.OrderValidator,
	transformer transformer.Transformer,
) *OrderHandler {
	return &OrderHandler{
		orderService: orderService,
		validator:    validator,
		transformer:  transformer,
	}
}

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

	var req contract.CreateOrderRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::CreateOrder", err.Error())}), "OrderHandler::CreateOrder")
		return
	}

	req.UserID = contextInfo.UserID
	modelReq := transformer.CreateOrderContractToModel(&req)
	response, err := h.orderService.CreateOrder(ctx, modelReq, contextInfo.OrganizationID)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::CreateOrder", err.Error())}), "OrderHandler::CreateOrder")
		return
	}

	contractResp := transformer.OrderModelToContract(response)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::CreateOrder")
}

func (h *OrderHandler) GetOrderByID(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_id", "OrderHandler::GetOrderByID", err.Error())}), "OrderHandler::GetOrderByID")
		return
	}

	response, err := h.orderService.GetOrderByID(c.Request.Context(), id)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::GetOrderByID", err.Error())}), "OrderHandler::GetOrderByID")
		return
	}

	contractResp := transformer.OrderModelToContract(response)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::GetOrderByID")
}

func (h *OrderHandler) UpdateOrder(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_id", "OrderHandler::UpdateOrder", err.Error())}), "OrderHandler::UpdateOrder")
		return
	}

	var req contract.UpdateOrderRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::UpdateOrder", err.Error())}), "OrderHandler::UpdateOrder")
		return
	}

	modelReq := transformer.UpdateOrderContractToModel(&req)
	response, err := h.orderService.UpdateOrder(c.Request.Context(), id, modelReq)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::UpdateOrder", err.Error())}), "OrderHandler::UpdateOrder")
		return
	}

	contractResp := transformer.OrderModelToContract(response)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::UpdateOrder")
}

func (h *OrderHandler) AddToOrder(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_id", "OrderHandler::AddToOrder", err.Error())}), "OrderHandler::AddToOrder")
		return
	}

	var req contract.AddToOrderRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::AddToOrder", err.Error())}), "OrderHandler::AddToOrder")
		return
	}

	modelReq := transformer.AddToOrderContractToModel(&req)
	response, err := h.orderService.AddToOrder(c.Request.Context(), id, modelReq)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::AddToOrder", err.Error())}), "OrderHandler::AddToOrder")
		return
	}

	contractResp := transformer.AddToOrderModelToContract(response)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::AddToOrder")
}

func (h *OrderHandler) ListOrders(c *gin.Context) {
	var query contract.ListOrdersQuery
	ctx := c.Request.Context()
	contextInfo := appcontext.FromGinContext(ctx)

	if err := c.ShouldBindQuery(&query); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_query_parameters", "OrderHandler::ListOrders", err.Error())}), "OrderHandler::ListOrders")
		return
	}

	modelReq := transformer.ListOrdersQueryToModel(&query)
	if err := h.validator.Validate(modelReq); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("validation_failed", "OrderHandler::ListOrders", err.Error())}), "OrderHandler::ListOrders")
		return
	}

	modelReq.OrganizationID = &contextInfo.OrganizationID
	response, err := h.orderService.ListOrders(c.Request.Context(), modelReq)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::ListOrders", err.Error())}), "OrderHandler::ListOrders")
		return
	}

	contractResp := transformer.ListOrdersModelToContract(response)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::ListOrders")
}

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

	var req contract.VoidOrderRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::VoidOrder", err.Error())}), "OrderHandler::VoidOrder")
		return
	}

	userID := contextInfo.UserID
	if err := h.validator.Validate(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("validation_failed", "OrderHandler::VoidOrder", err.Error())}), "OrderHandler::VoidOrder")
		return
	}

	modelReq := transformer.VoidOrderContractToModel(&req)
	if err := h.orderService.VoidOrder(ctx, modelReq, userID); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::VoidOrder", err.Error())}), "OrderHandler::VoidOrder")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(map[string]string{"message": "Order voided successfully"}), "OrderHandler::VoidOrder")
}

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

	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_id", "OrderHandler::RefundOrder", err.Error())}), "OrderHandler::RefundOrder")
		return
	}

	var req contract.RefundOrderRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::RefundOrder", err.Error())}), "OrderHandler::RefundOrder")
		return
	}

	userID := contextInfo.UserID

	modelReq := transformer.RefundOrderContractToModel(&req)
	if err := h.validator.Validate(modelReq); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("validation_failed", "OrderHandler::RefundOrder", err.Error())}), "OrderHandler::RefundOrder")
		return
	}

	if err := h.orderService.RefundOrder(ctx, id, modelReq, userID); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::RefundOrder", err.Error())}), "OrderHandler::RefundOrder")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(nil), "OrderHandler::RefundOrder")
}

func (h *OrderHandler) CreatePayment(c *gin.Context) {
	var req contract.CreatePaymentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::CreatePayment", err.Error())}), "OrderHandler::CreatePayment")
		return
	}

	if err := h.validator.Validate(req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("validation_failed", "OrderHandler::CreatePayment", err.Error())}), "OrderHandler::CreatePayment")
		return
	}

	modelReq := transformer.CreatePaymentContractToModel(&req)

	response, err := h.orderService.CreatePayment(c.Request.Context(), modelReq)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::CreatePayment", err.Error())}), "OrderHandler::CreatePayment")
		return
	}

	contractResp := transformer.PaymentModelToContract(response)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::CreatePayment")
}

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

	idStr := c.Param("id")
	paymentID, err := uuid.Parse(idStr)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_id", "OrderHandler::RefundPayment", err.Error())}), "OrderHandler::RefundPayment")
		return
	}

	var req contract.RefundPaymentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::RefundPayment", err.Error())}), "OrderHandler::RefundPayment")
		return
	}

	userID := contextInfo.UserID
	if userID == uuid.Nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("unauthorized", "OrderHandler::RefundPayment", "Invalid User ID in context")}), "OrderHandler::RefundPayment")
		return
	}

	if err := h.validator.Validate(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("validation_failed", "OrderHandler::RefundPayment", err.Error())}), "OrderHandler::RefundPayment")
		return
	}

	if err := h.orderService.RefundPayment(ctx, paymentID, req.RefundAmount, req.Reason, userID); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::RefundPayment", err.Error())}), "OrderHandler::RefundPayment")
		return
	}

	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(nil), "OrderHandler::RefundPayment")
}

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

	orderIDStr := c.Param("id")
	orderID, err := uuid.Parse(orderIDStr)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_id", "OrderHandler::SetOrderCustomer", "Invalid order ID")}), "OrderHandler::SetOrderCustomer")
		return
	}

	var req contract.SetOrderCustomerRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::SetOrderCustomer", err.Error())}), "OrderHandler::SetOrderCustomer")
		return
	}

	modelReq := transformer.SetOrderCustomerContractToModel(&req)

	response, err := h.orderService.SetOrderCustomer(ctx, orderID, modelReq, contextInfo.OrganizationID)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::SetOrderCustomer", err.Error())}), "OrderHandler::SetOrderCustomer")
		return
	}

	contractResp := transformer.SetOrderCustomerModelToContract(response)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::SetOrderCustomer")
}

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

	var req contract.SplitBillRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::SplitBill", err.Error())}), "OrderHandler::SplitBill")
		return
	}

	if err := h.validator.Validate(&req); err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("validation_failed", "OrderHandler::SplitBill", err.Error())}), "OrderHandler::SplitBill")
		return
	}

	req.OrganizationID = contextInfo.OrganizationID
	modelReq := transformer.SplitBillContractToModel(&req)
	response, err := h.orderService.SplitBill(c.Request.Context(), modelReq)
	if err != nil {
		util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::SplitBill", err.Error())}), "OrderHandler::SplitBill")
		return
	}

	contractResp := transformer.SplitBillModelToContract(response)
	util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::SplitBill")
}
