package handler

import (
	"io"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/warmbly/warmbly/internal/api/middleware"
	"github.com/warmbly/warmbly/internal/errx"
	"github.com/warmbly/warmbly/internal/models"
)

// GetSubscription returns the current organization's subscription
func (h *Handler) GetSubscription(c *gin.Context) {
	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	sub, errX := h.SubscriptionService.Get(c.Request.Context(), *orgID)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	c.JSON(http.StatusOK, sub)
}

// GetSubscriptionLimits returns the current organization's subscription with rate limits
func (h *Handler) GetSubscriptionLimits(c *gin.Context) {
	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	sub, errX := h.SubscriptionService.GetWithLimits(c.Request.Context(), *orgID)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	c.JSON(http.StatusOK, sub)
}

// ListPlans returns available subscription plans
func (h *Handler) ListPlans(c *gin.Context) {
	plans, errX := h.SubscriptionService.ListPlans(c.Request.Context(), true)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	c.JSON(http.StatusOK, gin.H{"plans": plans})
}

// CreateCheckoutSession creates a Stripe checkout session
func (h *Handler) CreateCheckoutSession(c *gin.Context) {
	userID := c.GetString("user_id")
	uid, err := uuid.Parse(userID)
	if err != nil {
		errx.JSON(c, errx.New(errx.Unauthorized, "invalid user"))
		return
	}

	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	var req struct {
		PriceID      string `json:"price_id" binding:"required"`
		SuccessURL   string `json:"success_url" binding:"required"`
		CancelURL    string `json:"cancel_url" binding:"required"`
		DiscountCode string `json:"discount_code"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
		return
	}

	session, errX := h.StripeService.CreateCheckoutSession(c.Request.Context(), uid, *orgID, req.PriceID, req.SuccessURL, req.CancelURL, req.DiscountCode)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	h.auditOrg(c, models.AuditActionCreate, models.AuditEntitySubscription, nil, nil, map[string]string{
		"action":   "checkout",
		"price_id": req.PriceID,
	})

	c.JSON(http.StatusOK, gin.H{
		"session_id":   session.ID,
		"checkout_url": session.URL,
	})
}

// CreateBillingPortalSession creates a Stripe billing portal session
func (h *Handler) CreateBillingPortalSession(c *gin.Context) {
	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	var req struct {
		ReturnURL string `json:"return_url" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
		return
	}

	sub, errX := h.SubscriptionService.Get(c.Request.Context(), *orgID)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	portalURL, errX := h.StripeService.CreatePortalSession(c.Request.Context(), sub.StripeCustomerID, req.ReturnURL)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	c.JSON(http.StatusOK, gin.H{"portal_url": portalURL})
}

// CancelSubscription cancels the current organization's subscription
func (h *Handler) CancelSubscription(c *gin.Context) {
	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	var req struct {
		CancelAtPeriodEnd bool `json:"cancel_at_period_end"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		errx.JSON(c, errx.ErrInvalid)
		return
	}

	sub, errX := h.SubscriptionService.Get(c.Request.Context(), *orgID)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	if sub.StripeSubscriptionID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no active subscription"))
		return
	}

	errX = h.StripeService.CancelSubscription(c.Request.Context(), *sub.StripeSubscriptionID, req.CancelAtPeriodEnd)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	subID := sub.ID
	action := models.AuditActionResume
	if req.CancelAtPeriodEnd {
		action = models.AuditActionStop
	}
	h.auditOrg(c, action, models.AuditEntitySubscription, &subID, nil, map[string]string{
		"plan":                 sub.PlanID.String(),
		"cancel_at_period_end": strconv.FormatBool(req.CancelAtPeriodEnd),
	})

	c.JSON(http.StatusOK, gin.H{"message": "subscription cancelled"})
}

// HandleStripeWebhook processes Stripe webhook events
func (h *Handler) HandleStripeWebhook(c *gin.Context) {
	payload, err := io.ReadAll(c.Request.Body)
	if err != nil {
		errx.JSON(c, errx.New(errx.BadRequest, "failed to read request body"))
		return
	}

	signature := c.GetHeader("Stripe-Signature")
	if signature == "" {
		errx.JSON(c, errx.New(errx.BadRequest, "missing stripe signature"))
		return
	}

	event, errX := h.StripeService.VerifyWebhook(payload, signature)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	errX = h.StripeService.ProcessWebhookEvent(c.Request.Context(), event)
	if errX != nil {
		// Return a retryable 5xx so Stripe re-delivers. The event was NOT
		// recorded as processed (see ProcessWebhookEvent), and every handler is
		// idempotent on the event id, so a retry safely re-runs without
		// double-applying. Silently 200-ing here would strand paid-for credits.
		c.JSON(http.StatusInternalServerError, gin.H{"received": false, "error": errX.Message})
		return
	}

	c.JSON(http.StatusOK, gin.H{"received": true})
}

// GetTrialStatus returns the current organization's free trial status
func (h *Handler) GetTrialStatus(c *gin.Context) {
	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	if h.TrialService == nil {
		errx.JSON(c, errx.New(errx.Internal, "trial service not available"))
		return
	}

	status, errX := h.TrialService.GetTrialStatus(c.Request.Context(), *orgID)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	c.JSON(http.StatusOK, status)
}

// GetFeatureStatus returns the current organization's feature access status
func (h *Handler) GetFeatureStatus(c *gin.Context) {
	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	if h.FeatureGateService == nil {
		errx.JSON(c, errx.New(errx.Internal, "feature gate service not available"))
		return
	}

	status, errX := h.FeatureGateService.GetSubscriptionStatus(c.Request.Context(), *orgID)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	// Add additional feature flags
	canSend, _ := h.FeatureGateService.CanSendCampaignEmail(c.Request.Context(), *orgID)
	canWarmup, _ := h.FeatureGateService.CanUseWarmup(c.Request.Context(), *orgID)
	canUnibox, _ := h.FeatureGateService.CanUseUnibox(c.Request.Context(), *orgID)

	c.JSON(http.StatusOK, gin.H{
		"subscription":       status,
		"can_send_campaigns": canSend,
		"can_use_warmup":     canWarmup,
		"can_use_unibox":     canUnibox,
	})
}

// ChangePlan changes the organization's subscription plan with proration
func (h *Handler) ChangePlan(c *gin.Context) {
	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	var req struct {
		PlanID            uuid.UUID `json:"plan_id" binding:"required"`
		ProrationBehavior string    `json:"proration_behavior"` // "create_prorations", "always_invoice", "none"
		DiscountCode      string    `json:"discount_code"`
		Interval          string    `json:"interval"` // "month" or "year"; defaults to monthly
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
		return
	}

	updated, errX := h.StripeService.ChangePlan(c.Request.Context(), *orgID, req.PlanID, req.ProrationBehavior, req.DiscountCode, req.Interval)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	planID := req.PlanID
	h.auditOrg(c, models.AuditActionUpdate, models.AuditEntitySubscription, &planID, nil, map[string]string{
		"action": "change_plan",
		"plan":   req.PlanID.String(),
	})

	c.JSON(http.StatusOK, gin.H{
		"message":      "plan changed successfully",
		"subscription": updated,
	})
}

// PreviewPlanChange previews the proration for a plan change
func (h *Handler) PreviewPlanChange(c *gin.Context) {
	orgID := middleware.GetOrganizationID(c)
	if orgID == nil {
		errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
		return
	}

	newPlanIDStr := c.Query("new_plan_id")
	if newPlanIDStr == "" {
		errx.JSON(c, errx.New(errx.BadRequest, "new_plan_id is required"))
		return
	}

	newPlanID, err := uuid.Parse(newPlanIDStr)
	if err != nil {
		errx.JSON(c, errx.New(errx.BadRequest, "invalid plan ID"))
		return
	}

	preview, errX := h.StripeService.PreviewPlanChange(c.Request.Context(), *orgID, newPlanID)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	c.JSON(http.StatusOK, preview)
}

// EnterpriseInquiryRequest represents a request for enterprise pricing
type EnterpriseInquiryRequest struct {
	CompanyName     string `json:"company_name" binding:"required"`
	ContactName     string `json:"contact_name" binding:"required"`
	ContactEmail    string `json:"contact_email" binding:"required,email"`
	EstimatedVolume *int   `json:"estimated_volume,omitempty"`
	TeamSize        *int   `json:"team_size,omitempty"`
	Notes           string `json:"notes,omitempty"`
}

// SubmitEnterpriseInquiry submits an enterprise pricing inquiry
func (h *Handler) SubmitEnterpriseInquiry(c *gin.Context) {
	var req EnterpriseInquiryRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
		return
	}

	inquiry := &models.EnterpriseInquiry{
		CompanyName:     req.CompanyName,
		ContactName:     req.ContactName,
		ContactEmail:    req.ContactEmail,
		EstimatedVolume: req.EstimatedVolume,
		TeamSize:        req.TeamSize,
		Notes:           req.Notes,
	}

	// Get organization ID if available (user might be authenticated)
	orgID := middleware.GetOrganizationID(c)
	if orgID != nil {
		// Could track which organization made the inquiry
		inquiry.Notes = inquiry.Notes + " [Organization: " + orgID.String() + "]"
	}

	created, errX := h.OrganizationService.CreateEnterpriseInquiry(c.Request.Context(), inquiry)
	if errX != nil {
		errx.JSON(c, errX)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message":    "Thank you! Our team will contact you within 24 hours.",
		"inquiry_id": created.ID,
	})
}
