package handlers

import (
	"Yimaru-Backend/internal/domain"
	"strconv"

	"github.com/gofiber/fiber/v2"
)

// =====================
// Payment Types
// =====================

type initiatePaymentReq struct {
	PlanID int64  `json:"plan_id" validate:"required"`
	Phone  string `json:"phone" validate:"required"`
	Email  string `json:"email" validate:"required,email"`
}

type paymentRes struct {
	ID             int64   `json:"id"`
	UserID         int64   `json:"user_id"`
	PlanID         *int64  `json:"plan_id,omitempty"`
	SubscriptionID *int64  `json:"subscription_id,omitempty"`
	SessionID      *string `json:"session_id,omitempty"`
	Amount         float64 `json:"amount"`
	Currency       string  `json:"currency"`
	PaymentMethod  *string `json:"payment_method,omitempty"`
	Status         string  `json:"status"`
	PaymentURL     *string `json:"payment_url,omitempty"`
	PlanName       *string `json:"plan_name,omitempty"`
	PaidAt         *string `json:"paid_at,omitempty"`
	ExpiresAt      *string `json:"expires_at,omitempty"`
	CreatedAt      string  `json:"created_at"`
}

// =====================
// Subscription Payment Handlers
// =====================

// InitiateSubscriptionPayment godoc
// @Summary Initiate subscription payment
// @Description Creates a payment session for a subscription plan
// @Tags payments
// @Accept json
// @Produce json
// @Param body body initiatePaymentReq true "Payment request"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/payments/subscribe [post]
func (h *Handler) InitiateSubscriptionPayment(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	var req initiatePaymentReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	result, err := h.arifpaySvc.InitiateSubscriptionPayment(c.Context(), userID, domain.InitiateSubscriptionPaymentRequest{
		PlanID: req.PlanID,
		Phone:  req.Phone,
		Email:  req.Email,
	})
	if err != nil {
		status := fiber.StatusInternalServerError
		if err.Error() == "user already has an active subscription" {
			status = fiber.StatusConflict
		}
		return c.Status(status).JSON(domain.ErrorResponse{
			Message: "Failed to initiate payment",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Payment initiated successfully",
		Data:    result,
	})
}

// VerifyPayment godoc
// @Summary Verify payment status
// @Description Checks the payment status with the payment provider
// @Tags payments
// @Produce json
// @Param session_id path string true "Session ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Router /api/v1/payments/verify/{session_id} [get]
func (h *Handler) VerifyPayment(c *fiber.Ctx) error {
	sessionID := c.Params("session_id")
	if sessionID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Session ID is required",
		})
	}

	payment, err := h.arifpaySvc.VerifyPayment(c.Context(), sessionID)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Payment not found or verification failed",
			Error:   err.Error(),
		})
	}

	if payment.Status == string(domain.PaymentStatusSuccess) {
		h.sendInAppNotification(payment.UserID, domain.NOTIFICATION_TYPE_PAYMENT_VERIFIED, "Payment Successful", "Your payment has been verified successfully. Your subscription is now active.")
	}

	return c.JSON(domain.Response{
		Message: "Payment status retrieved",
		Data:    paymentToRes(payment),
	})
}

// GetMyPayments godoc
// @Summary Get payment history
// @Description Returns the authenticated user's payment history
// @Tags payments
// @Produce json
// @Param limit query int false "Limit" default(20)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} domain.Response
// @Router /api/v1/payments [get]
func (h *Handler) GetMyPayments(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	limit, _ := strconv.Atoi(c.Query("limit", "20"))
	offset, _ := strconv.Atoi(c.Query("offset", "0"))

	payments, err := h.arifpaySvc.GetPaymentsByUser(c.Context(), userID, int32(limit), int32(offset))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to get payment history",
			Error:   err.Error(),
		})
	}

	result := make([]paymentRes, len(payments))
	for i, p := range payments {
		result[i] = *paymentToRes(&p)
	}

	return c.JSON(domain.Response{
		Message: "Payment history retrieved successfully",
		Data:    result,
	})
}

// GetPaymentByID godoc
// @Summary Get payment details
// @Description Returns details of a specific payment
// @Tags payments
// @Produce json
// @Param id path int true "Payment ID"
// @Success 200 {object} domain.Response
// @Failure 404 {object} domain.ErrorResponse
// @Router /api/v1/payments/{id} [get]
func (h *Handler) GetPaymentByID(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	id, err := strconv.ParseInt(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid payment ID",
		})
	}

	payment, err := h.arifpaySvc.GetPaymentByID(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Payment not found",
		})
	}

	if payment.UserID != userID {
		return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
			Message: "Access denied",
		})
	}

	return c.JSON(domain.Response{
		Message: "Payment retrieved successfully",
		Data:    paymentToRes(payment),
	})
}

// CancelPayment godoc
// @Summary Cancel a pending payment
// @Description Cancels a payment that is still pending
// @Tags payments
// @Produce json
// @Param id path int true "Payment ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Router /api/v1/payments/{id}/cancel [post]
func (h *Handler) CancelPayment(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	id, err := strconv.ParseInt(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid payment ID",
		})
	}

	if err := h.arifpaySvc.CancelPayment(c.Context(), id, userID); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Failed to cancel payment",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Payment cancelled successfully",
	})
}

// HandleArifpayWebhook godoc
// @Summary Handle ArifPay webhook
// @Description Processes payment notifications from ArifPay
// @Tags payments
// @Accept json
// @Produce json
// @Param body body domain.WebhookRequest true "Webhook payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Router /api/v1/payments/webhook [post]
func (h *Handler) HandleArifpayWebhook(c *fiber.Ctx) error {
	var req domain.WebhookRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid webhook payload",
			Error:   err.Error(),
		})
	}

	if err := h.arifpaySvc.ProcessPaymentWebhook(c.Context(), req); err != nil {
		h.logger.Error("Failed to process webhook", "error", err, "nonce", req.Nonce)
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to process webhook",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Webhook processed successfully",
	})
}

// GetArifpayPaymentMethods godoc
// @Summary Get available payment methods
// @Description Returns list of supported ArifPay payment methods
// @Tags payments
// @Produce json
// @Success 200 {object} domain.Response
// @Router /api/v1/payments/methods [get]
func (h *Handler) GetArifpayPaymentMethods(c *fiber.Ctx) error {
	methods := h.arifpaySvc.GetPaymentMethodsMapping()

	return c.JSON(domain.Response{
		Message: "Payment methods retrieved successfully",
		Data:    methods,
	})
}

// =====================
// Direct Payment Handlers
// =====================

type initiateDirectPaymentReq struct {
	PlanID        int64  `json:"plan_id" validate:"required"`
	Phone         string `json:"phone" validate:"required"`
	Email         string `json:"email" validate:"required,email"`
	PaymentMethod string `json:"payment_method" validate:"required"`
}

type verifyOTPReq struct {
	SessionID string `json:"session_id" validate:"required"`
	OTP       string `json:"otp" validate:"required"`
}

// InitiateDirectPayment godoc
// @Summary Initiate direct payment
// @Description Creates a payment session and initiates direct payment (OTP-based)
// @Tags payments
// @Accept json
// @Produce json
// @Param body body initiateDirectPaymentReq true "Direct payment request"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/payments/direct [post]
func (h *Handler) InitiateDirectPayment(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	var req initiateDirectPaymentReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	result, err := h.arifpaySvc.InitiateDirectPayment(c.Context(), userID, domain.InitiateDirectPaymentRequest{
		PlanID:        req.PlanID,
		Phone:         req.Phone,
		Email:         req.Email,
		PaymentMethod: domain.DirectPaymentMethod(req.PaymentMethod),
	})
	if err != nil {
		status := fiber.StatusInternalServerError
		if err.Error() == "user already has an active subscription" {
			status = fiber.StatusConflict
		}
		return c.Status(status).JSON(domain.ErrorResponse{
			Message: "Failed to initiate direct payment",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: result.Message,
		Data:    result,
	})
}

// VerifyDirectPaymentOTP godoc
// @Summary Verify OTP for direct payment
// @Description Verifies the OTP sent for direct payment methods (Amole, HelloCash, etc.)
// @Tags payments
// @Accept json
// @Produce json
// @Param body body verifyOTPReq true "OTP verification request"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Router /api/v1/payments/direct/verify-otp [post]
func (h *Handler) VerifyDirectPaymentOTP(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	var req verifyOTPReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	result, err := h.arifpaySvc.VerifyDirectPaymentOTP(c.Context(), userID, domain.VerifyOTPRequest{
		SessionID: req.SessionID,
		OTP:       req.OTP,
	})
	if err != nil {
		status := fiber.StatusInternalServerError
		if err.Error() == "payment not found" {
			status = fiber.StatusNotFound
		}
		return c.Status(status).JSON(domain.ErrorResponse{
			Message: "Failed to verify OTP",
			Error:   err.Error(),
		})
	}

	if !result.Success {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: result.Message,
		})
	}

	return c.JSON(domain.Response{
		Message: result.Message,
		Data:    result,
	})
}

// GetDirectPaymentMethods godoc
// @Summary Get direct payment methods
// @Description Returns list of payment methods that support direct payment (OTP-based)
// @Tags payments
// @Produce json
// @Success 200 {object} domain.Response
// @Router /api/v1/payments/direct/methods [get]
func (h *Handler) GetDirectPaymentMethods(c *fiber.Ctx) error {
	methods := h.arifpaySvc.GetDirectPaymentMethods()

	return c.JSON(domain.Response{
		Message: "Direct payment methods retrieved successfully",
		Data:    methods,
	})
}

// Helper functions

func paymentToRes(p *domain.Payment) *paymentRes {
	res := &paymentRes{
		ID:             p.ID,
		UserID:         p.UserID,
		PlanID:         p.PlanID,
		SubscriptionID: p.SubscriptionID,
		SessionID:      p.SessionID,
		Amount:         p.Amount,
		Currency:       p.Currency,
		PaymentMethod:  p.PaymentMethod,
		Status:         p.Status,
		PaymentURL:     p.PaymentURL,
		PlanName:       p.PlanName,
		CreatedAt:      p.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
	}

	if p.PaidAt != nil {
		t := p.PaidAt.Format("2006-01-02T15:04:05Z07:00")
		res.PaidAt = &t
	}
	if p.ExpiresAt != nil {
		t := p.ExpiresAt.Format("2006-01-02T15:04:05Z07:00")
		res.ExpiresAt = &t
	}

	return res
}
