package handlers

import (
	"context"
	"encoding/json"
	"fmt"
	"io"

	"github.com/gin-gonic/gin"
	"github.com/jackc/pgx/v5/pgxpool"
	"ticketing-backend/internal/models"
	"ticketing-backend/internal/services"
	"ticketing-backend/internal/utils"
)

type PaymentHandler struct {
	pool           *pgxpool.Pool
	paymentService *services.PaymentService
	orderService   *services.OrderService
	ticketService  *services.TicketService
}

func NewPaymentHandler(pool *pgxpool.Pool, ps *services.PaymentService, os *services.OrderService, ts *services.TicketService) *PaymentHandler {
	return &PaymentHandler{pool: pool, paymentService: ps, orderService: os, ticketService: ts}
}

func (h *PaymentHandler) CreatePayment(c *gin.Context) {
	var req models.CreatePaymentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.BadRequest(c, "orderId is required")
		return
	}

	userID, _ := c.Get("userId")

	// Get order details
	type orderInfo struct {
		EventID       string
		TotalAmount   float64
		BuyerName     string
		BuyerEmail    string
		Status        string
		BuyerID       string
		DiscountAmount *float64
	}
	var order orderInfo
	err := h.pool.QueryRow(c.Request.Context(),
		`SELECT event_id, total_amount, buyer_name, buyer_email, status, buyer_id, discount_amount
		 FROM orders WHERE id = $1`, req.OrderID,
	).Scan(&order.EventID, &order.TotalAmount, &order.BuyerName, &order.BuyerEmail, &order.Status, &order.BuyerID, &order.DiscountAmount)
	if err != nil {
		utils.NotFound(c, "Order not found")
		return
	}

	if order.BuyerID != userID.(string) {
		utils.Forbidden(c, "This order does not belong to you")
		return
	}

	if order.Status != "pending" {
		utils.BadRequest(c, fmt.Sprintf("Order status is '%s', cannot create payment", order.Status))
		return
	}

	// If total amount is 0 or free event
	if order.TotalAmount <= 0 {
		payment, err := h.paymentService.CreatePayment(req.OrderID, 0, order.BuyerEmail, order.BuyerName)
		if err == nil && payment.Status == "completed" {
			h.orderService.CompleteOrder(req.OrderID)
			utils.Success(c, gin.H{
				"payment":         payment,
				"orderCompleted":  true,
				"paymentRequired": false,
			})
			return
		}
	}

	payment, err := h.paymentService.CreatePayment(req.OrderID, order.TotalAmount, order.BuyerEmail, order.BuyerName)
	if err != nil {
		utils.InternalError(c, "Failed to create payment: "+err.Error())
		return
	}

	utils.Success(c, gin.H{
		"payment":         payment,
		"paymentRequired": true,
	})
}

func (h *PaymentHandler) GetPaymentStatus(c *gin.Context) {
	paymentID := c.Query("paymentId")
	if paymentID == "" {
		utils.BadRequest(c, "paymentId is required")
		return
	}

	payment, err := h.paymentService.GetPaymentStatus(paymentID)
	if err != nil {
		utils.NotFound(c, "Payment not found")
		return
	}

	utils.Success(c, gin.H{"payment": payment})
}

func (h *PaymentHandler) Webhook(c *gin.Context) {
	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		utils.BadRequest(c, "Failed to read request body")
		return
	}

	signature := c.GetHeader("X-Midtrans-Signature")
	if signature == "" {
		signature = c.GetHeader("x-midtrans-signature")
	}

	// Verify webhook signature in production
	// if s.isProduction && !h.paymentService.VerifyWebhookSignature(body, signature) {
	//     utils.Forbidden(c, "Invalid signature")
	//     return
	// }

	var notif services.MidtransNotification
	if err := json.Unmarshal(body, &notif); err != nil {
		utils.BadRequest(c, "Invalid webhook payload")
		return
	}

	status, err := h.paymentService.HandleWebhook(&notif)
	if err != nil {
		utils.InternalError(c, "Failed to process webhook")
		return
	}

	// Process based on payment status
	switch status {
	case "completed":
		h.processCompletedPayment(notif.OrderID, notif.TransactionID)
	case "expired":
		h.orderService.CancelOrder(notif.OrderID)
	}

	utils.Success(c, gin.H{"status": status})
}

// processCompletedPayment handles successful payment: update payment, complete order, generate tickets
func (h *PaymentHandler) processCompletedPayment(orderID, transactionID string) {
	ctx := context.Background()

	// Update payment status
	var paymentID string
	err := h.pool.QueryRow(ctx,
		`UPDATE payments SET status = 'completed', gateway_transaction_id = $2, updated_at = NOW()
		 WHERE order_id = $1 AND status = 'pending'
		 RETURNING id`,
		orderID, transactionID,
	).Scan(&paymentID)
	if err != nil {
		return
	}

	// Complete order (deducts capacity atomically)
	err = h.orderService.CompleteOrder(orderID)
	if err != nil {
		return
	}

	// Generate tickets
	var eventID, ticketClassID, buyerID string
	var quantity int
	err = h.pool.QueryRow(ctx,
		`SELECT event_id, ticket_class_id, buyer_id, quantity FROM orders WHERE id = $1`, orderID,
	).Scan(&eventID, &ticketClassID, &buyerID, &quantity)
	if err == nil {
		h.ticketService.GenerateTickets(orderID, &buyerID, eventID, ticketClassID, quantity)
	}
}

func (h *PaymentHandler) MarkPaid(c *gin.Context) {
	paymentID := c.Param("paymentId")

	err := h.paymentService.CompletePayment(paymentID, "")
	if err != nil {
		utils.InternalError(c, "Failed to mark payment as paid")
		return
	}

	var orderID string
	h.pool.QueryRow(c.Request.Context(),
		`SELECT order_id FROM payments WHERE id = $1`, paymentID,
	).Scan(&orderID)

	if orderID != "" {
		h.processCompletedPayment(orderID, "")
	}

	utils.Success(c, gin.H{"message": "Payment marked as paid"})
}
