package handlers

import (
	"net/http"

	"github.com/breakpilot/billing-service/internal/database"
	"github.com/breakpilot/billing-service/internal/middleware"
	"github.com/breakpilot/billing-service/internal/models"
	"github.com/breakpilot/billing-service/internal/services"
	"github.com/gin-gonic/gin"
)

// BillingHandler handles billing-related HTTP requests
type BillingHandler struct {
	db                  *database.DB
	subscriptionService *services.SubscriptionService
	stripeService       *services.StripeService
	entitlementService  *services.EntitlementService
	usageService        *services.UsageService
}

// NewBillingHandler creates a new BillingHandler
func NewBillingHandler(
	db *database.DB,
	subscriptionService *services.SubscriptionService,
	stripeService *services.StripeService,
	entitlementService *services.EntitlementService,
	usageService *services.UsageService,
) *BillingHandler {
	return &BillingHandler{
		db:                  db,
		subscriptionService: subscriptionService,
		stripeService:       stripeService,
		entitlementService:  entitlementService,
		usageService:        usageService,
	}
}

// GetBillingStatus returns the current billing status for a user
// GET /api/v1/billing/status
func (h *BillingHandler) GetBillingStatus(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID.String() == "" {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error":   "unauthorized",
			"message": "User not authenticated",
		})
		return
	}

	ctx := c.Request.Context()

	// Get subscription
	subscription, err := h.subscriptionService.GetByUserID(ctx, userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "internal_error",
			"message": "Failed to get subscription",
		})
		return
	}

	// Get available plans
	plans, err := h.subscriptionService.GetAvailablePlans(ctx)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "internal_error",
			"message": "Failed to get plans",
		})
		return
	}

	response := models.BillingStatusResponse{
		HasSubscription: subscription != nil,
		AvailablePlans:  plans,
	}

	if subscription != nil {
		// Get plan details
		plan, _ := h.subscriptionService.GetPlanByID(ctx, string(subscription.PlanID))

		subInfo := &models.SubscriptionInfo{
			PlanID:            subscription.PlanID,
			Status:            subscription.Status,
			IsTrialing:        subscription.Status == models.StatusTrialing,
			CancelAtPeriodEnd: subscription.CancelAtPeriodEnd,
			CurrentPeriodEnd:  subscription.CurrentPeriodEnd,
		}

		if plan != nil {
			subInfo.PlanName = plan.Name
			subInfo.PriceCents = plan.PriceCents
			subInfo.Currency = plan.Currency
		}

		// Calculate trial days left
		if subscription.TrialEnd != nil && subscription.Status == models.StatusTrialing {
			// TODO: Calculate days left
		}

		response.Subscription = subInfo

		// Get task usage info (legacy usage tracking - see TaskService for new task-based usage)
		// TODO: Replace with TaskService.GetTaskUsageInfo for task-based billing
		_, _ = h.usageService.GetUsageSummary(ctx, userID)

		// Get entitlements
		entitlements, _ := h.entitlementService.GetEntitlements(ctx, userID)
		if entitlements != nil {
			response.Entitlements = entitlements
		}
	}

	c.JSON(http.StatusOK, response)
}

// GetPlans returns all available billing plans
// GET /api/v1/billing/plans
func (h *BillingHandler) GetPlans(c *gin.Context) {
	ctx := c.Request.Context()

	plans, err := h.subscriptionService.GetAvailablePlans(ctx)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "internal_error",
			"message": "Failed to get plans",
		})
		return
	}

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

// StartTrial starts a trial for the user with a specific plan
// POST /api/v1/billing/trial/start
func (h *BillingHandler) StartTrial(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID.String() == "" {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error":   "unauthorized",
			"message": "User not authenticated",
		})
		return
	}

	var req models.StartTrialRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "invalid_request",
			"message": "Invalid request body",
		})
		return
	}

	ctx := c.Request.Context()

	// Check if user already has a subscription
	existing, _ := h.subscriptionService.GetByUserID(ctx, userID)
	if existing != nil {
		c.JSON(http.StatusConflict, gin.H{
			"error":   "subscription_exists",
			"message": "User already has a subscription",
		})
		return
	}

	// Get user email from context
	email, _ := c.Get("email")
	emailStr, _ := email.(string)

	// Create Stripe checkout session
	checkoutURL, sessionID, err := h.stripeService.CreateCheckoutSession(ctx, userID, emailStr, req.PlanID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "stripe_error",
			"message": "Failed to create checkout session",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, models.StartTrialResponse{
		CheckoutURL: checkoutURL,
		SessionID:   sessionID,
	})
}

// ChangePlan changes the user's subscription plan
// POST /api/v1/billing/change-plan
func (h *BillingHandler) ChangePlan(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID.String() == "" {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error":   "unauthorized",
			"message": "User not authenticated",
		})
		return
	}

	var req models.ChangePlanRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "invalid_request",
			"message": "Invalid request body",
		})
		return
	}

	ctx := c.Request.Context()

	// Get current subscription
	subscription, err := h.subscriptionService.GetByUserID(ctx, userID)
	if err != nil || subscription == nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error":   "no_subscription",
			"message": "No active subscription found",
		})
		return
	}

	// Change plan via Stripe
	err = h.stripeService.ChangePlan(ctx, subscription.StripeSubscriptionID, req.NewPlanID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "stripe_error",
			"message": "Failed to change plan",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, models.ChangePlanResponse{
		Success: true,
		Message: "Plan changed successfully",
	})
}

// CancelSubscription cancels the user's subscription at period end
// POST /api/v1/billing/cancel
func (h *BillingHandler) CancelSubscription(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID.String() == "" {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error":   "unauthorized",
			"message": "User not authenticated",
		})
		return
	}

	ctx := c.Request.Context()

	// Get current subscription
	subscription, err := h.subscriptionService.GetByUserID(ctx, userID)
	if err != nil || subscription == nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error":   "no_subscription",
			"message": "No active subscription found",
		})
		return
	}

	// Cancel at period end via Stripe
	err = h.stripeService.CancelSubscription(ctx, subscription.StripeSubscriptionID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "stripe_error",
			"message": "Failed to cancel subscription",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, models.CancelSubscriptionResponse{
		Success: true,
		Message: "Subscription will be canceled at the end of the billing period",
	})
}

// GetCustomerPortal returns a URL to the Stripe customer portal
// GET /api/v1/billing/portal
func (h *BillingHandler) GetCustomerPortal(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID.String() == "" {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error":   "unauthorized",
			"message": "User not authenticated",
		})
		return
	}

	ctx := c.Request.Context()

	// Get current subscription
	subscription, err := h.subscriptionService.GetByUserID(ctx, userID)
	if err != nil || subscription == nil || subscription.StripeCustomerID == "" {
		c.JSON(http.StatusNotFound, gin.H{
			"error":   "no_subscription",
			"message": "No active subscription found",
		})
		return
	}

	// Create portal session
	portalURL, err := h.stripeService.CreateCustomerPortalSession(ctx, subscription.StripeCustomerID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "stripe_error",
			"message": "Failed to create portal session",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, models.CustomerPortalResponse{
		PortalURL: portalURL,
	})
}

// =============================================
// Internal Endpoints (Service-to-Service)
// =============================================

// GetEntitlements returns entitlements for a user (internal)
// GET /api/v1/billing/entitlements/:userId
func (h *BillingHandler) GetEntitlements(c *gin.Context) {
	userIDStr := c.Param("userId")

	ctx := c.Request.Context()

	entitlements, err := h.entitlementService.GetEntitlementsByUserIDString(ctx, userIDStr)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "internal_error",
			"message": "Failed to get entitlements",
		})
		return
	}

	if entitlements == nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error":   "not_found",
			"message": "No entitlements found for user",
		})
		return
	}

	c.JSON(http.StatusOK, entitlements)
}

// TrackUsage tracks usage for a user (internal)
// POST /api/v1/billing/usage/track
func (h *BillingHandler) TrackUsage(c *gin.Context) {
	var req models.TrackUsageRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "invalid_request",
			"message": "Invalid request body",
		})
		return
	}

	ctx := c.Request.Context()

	quantity := req.Quantity
	if quantity <= 0 {
		quantity = 1
	}

	err := h.usageService.TrackUsage(ctx, req.UserID, req.UsageType, quantity)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "internal_error",
			"message": "Failed to track usage",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Usage tracked",
	})
}

// CheckUsage checks if usage is allowed (internal)
// GET /api/v1/billing/usage/check/:userId/:type
func (h *BillingHandler) CheckUsage(c *gin.Context) {
	userIDStr := c.Param("userId")
	usageType := c.Param("type")

	ctx := c.Request.Context()

	response, err := h.usageService.CheckUsageAllowed(ctx, userIDStr, usageType)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "internal_error",
			"message": "Failed to check usage",
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

// CheckEntitlement checks if a user has a specific entitlement (internal)
// GET /api/v1/billing/entitlements/check/:userId/:feature
func (h *BillingHandler) CheckEntitlement(c *gin.Context) {
	userIDStr := c.Param("userId")
	feature := c.Param("feature")

	ctx := c.Request.Context()

	hasEntitlement, planID, err := h.entitlementService.CheckEntitlement(ctx, userIDStr, feature)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "internal_error",
			"message": "Failed to check entitlement",
		})
		return
	}

	c.JSON(http.StatusOK, models.EntitlementCheckResponse{
		HasEntitlement: hasEntitlement,
		PlanID:         planID,
	})
}
