package handler

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	appservice "github.com/genex/trading-service/internal/application/service"
	"github.com/genex/trading-service/internal/domain/vo"
)

// TradeHandler handles user-facing trading HTTP endpoints.
type TradeHandler struct {
	tradeService *appservice.TradeService
}

// NewTradeHandler creates a new TradeHandler with injected trade service.
func NewTradeHandler(tradeService *appservice.TradeService) *TradeHandler {
	return &TradeHandler{tradeService: tradeService}
}

// PlaceOrderReq is the request body for placing an order.
type PlaceOrderReq struct {
	CouponID string  `json:"couponId" binding:"required"`
	Side     string  `json:"side" binding:"required,oneof=buy sell"`
	Type     string  `json:"type" binding:"required,oneof=limit market"`
	Price    float64 `json:"price"`
	Quantity int     `json:"quantity" binding:"required,min=1"`
}

// PlaceOrder handles POST /api/v1/trades/orders
func (h *TradeHandler) PlaceOrder(c *gin.Context) {
	var req PlaceOrderReq
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
		return
	}

	// Validate and convert to value objects
	side, err := vo.NewOrderSide(req.Side)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
		return
	}

	orderType, err := vo.NewOrderType(req.Type)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
		return
	}

	price, err := vo.NewPrice(req.Price)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
		return
	}

	quantity, err := vo.NewQuantity(req.Quantity)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
		return
	}

	userID := c.GetString("userId")

	output, err := h.tradeService.PlaceOrder(c.Request.Context(), appservice.PlaceOrderInput{
		UserID:   userID,
		CouponID: req.CouponID,
		Side:     side,
		Type:     orderType,
		Price:    price,
		Quantity: quantity,
	})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": -1, "message": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"code": 0, "data": gin.H{
		"order":  output.Order,
		"trades": output.Trades,
	}})
}

// CancelOrder handles DELETE /api/v1/trades/orders/:id
func (h *TradeHandler) CancelOrder(c *gin.Context) {
	couponID := c.Query("couponId")
	orderID := c.Param("id")
	sideStr := c.Query("side")

	side, err := vo.NewOrderSide(sideStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
		return
	}

	err = h.tradeService.CancelOrder(c.Request.Context(), couponID, orderID, side)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"code": -1, "message": "Order not found"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"code": 0, "data": nil})
}

// GetOrderBook handles GET /api/v1/trades/orderbook/:couponId
func (h *TradeHandler) GetOrderBook(c *gin.Context) {
	couponID := c.Param("couponId")
	depth, _ := strconv.Atoi(c.DefaultQuery("depth", "20"))

	bids, asks := h.tradeService.GetOrderBookSnapshot(couponID, depth)
	c.JSON(http.StatusOK, gin.H{"code": 0, "data": gin.H{
		"couponId": couponID,
		"bids":     bids,
		"asks":     asks,
	}})
}

// MyOrders handles GET /api/v1/trades/my/orders — paginated list of current user's orders.
func (h *TradeHandler) MyOrders(c *gin.Context) {
	userID := c.GetString("userId")

	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
	status := c.Query("status")

	if page < 1 {
		page = 1
	}
	if limit < 1 || limit > 100 {
		limit = 20
	}

	orders, total, err := h.tradeService.GetOrdersByUserPaginated(c.Request.Context(), userID, status, page, limit)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": -1, "message": err.Error()})
		return
	}

	type orderItem struct {
		ID          string  `json:"id"`
		CouponID    string  `json:"couponId"`
		CouponName  string  `json:"couponName"`
		Type        string  `json:"type"`
		Quantity    int     `json:"quantity"`
		Price       float64 `json:"price"`
		TotalAmount float64 `json:"totalAmount"`
		Status      string  `json:"status"`
		CreatedAt   string  `json:"createdAt"`
	}

	items := make([]orderItem, len(orders))
	for i, o := range orders {
		items[i] = orderItem{
			ID:          o.ID,
			CouponID:    o.CouponID,
			CouponName:  o.CouponID, // coupon name would come from coupon service; use ID as placeholder
			Type:        o.Side.String(),
			Quantity:    o.Quantity.Int(),
			Price:       o.Price.Float64(),
			TotalAmount: o.Price.Float64() * float64(o.Quantity.Int()),
			Status:      string(o.Status),
			CreatedAt:   o.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
		}
	}

	c.JSON(http.StatusOK, gin.H{"code": 0, "data": gin.H{
		"orders": items,
		"total":  total,
		"page":   page,
		"limit":  limit,
	}})
}

// TransferCouponReq is the request body for transferring a coupon.
type TransferCouponReq struct {
	RecipientID    string `json:"recipientId"`
	RecipientPhone string `json:"recipientPhone"`
}

// TransferCoupon handles POST /api/v1/trades/coupons/:id/transfer — transfer coupon ownership.
func (h *TradeHandler) TransferCoupon(c *gin.Context) {
	couponID := c.Param("id")
	userID := c.GetString("userId")

	var req TransferCouponReq
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
		return
	}

	recipientID := req.RecipientID
	if recipientID == "" && req.RecipientPhone == "" {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": "recipientId or recipientPhone is required"})
		return
	}

	// If recipientPhone is provided but no recipientId, use phone as placeholder ID.
	// In production, this would look up the user by phone via user-service.
	if recipientID == "" {
		recipientID = "phone:" + req.RecipientPhone
	}

	if recipientID == userID {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": "cannot transfer to yourself"})
		return
	}

	err := h.tradeService.TransferCoupon(c.Request.Context(), couponID, userID, recipientID)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"code": 0, "data": gin.H{
		"couponId":    couponID,
		"recipientId": recipientID,
		"status":      "transferred",
	}})
}
