package handlers

import (
	"Yimaru-Backend/internal/domain"
	"context"
	"encoding/json"
	"fmt"
	"strconv"

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

// =====================
// Subscription Plan Types
// =====================

type createPlanReq struct {
	Name          string  `json:"name" validate:"required"`
	Description   *string `json:"description"`
	DurationValue int32   `json:"duration_value" validate:"required,min=1"`
	DurationUnit  string  `json:"duration_unit" validate:"required,oneof=DAY WEEK MONTH YEAR"`
	Price         float64 `json:"price" validate:"required,min=0"`
	Currency      string  `json:"currency" validate:"required"`
	IsActive      *bool   `json:"is_active"`
}

type updatePlanReq struct {
	Name          *string  `json:"name"`
	Description   *string  `json:"description"`
	DurationValue *int32   `json:"duration_value"`
	DurationUnit  *string  `json:"duration_unit"`
	Price         *float64 `json:"price"`
	Currency      *string  `json:"currency"`
	IsActive      *bool    `json:"is_active"`
}

type planRes struct {
	ID            int64   `json:"id"`
	Name          string  `json:"name"`
	Description   *string `json:"description,omitempty"`
	DurationValue int32   `json:"duration_value"`
	DurationUnit  string  `json:"duration_unit"`
	Price         float64 `json:"price"`
	Currency      string  `json:"currency"`
	IsActive      bool    `json:"is_active"`
	CreatedAt     string  `json:"created_at"`
}

// =====================
// User Subscription Types
// =====================

type subscribeReq struct {
	PlanID           int64   `json:"plan_id" validate:"required"`
	PaymentReference *string `json:"payment_reference"`
	PaymentMethod    *string `json:"payment_method"`
}

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

type subscriptionRes struct {
	ID               int64    `json:"id"`
	UserID           int64    `json:"user_id"`
	PlanID           int64    `json:"plan_id"`
	PlanName         *string  `json:"plan_name,omitempty"`
	StartsAt         string   `json:"starts_at"`
	ExpiresAt        string   `json:"expires_at"`
	Status           string   `json:"status"`
	PaymentReference *string  `json:"payment_reference,omitempty"`
	PaymentMethod    *string  `json:"payment_method,omitempty"`
	AutoRenew        bool     `json:"auto_renew"`
	DurationValue    *int32   `json:"duration_value,omitempty"`
	DurationUnit     *string  `json:"duration_unit,omitempty"`
	Price            *float64 `json:"price,omitempty"`
	Currency         *string  `json:"currency,omitempty"`
	CreatedAt        string   `json:"created_at"`
}

type autoRenewReq struct {
	AutoRenew bool `json:"auto_renew"`
}

// =====================
// Subscription Plan Handlers
// =====================

// CreateSubscriptionPlan godoc
// @Summary Create a subscription plan
// @Description Creates a new subscription plan (admin only)
// @Tags subscriptions
// @Accept json
// @Produce json
// @Param body body createPlanReq true "Create plan payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/subscription-plans [post]
func (h *Handler) CreateSubscriptionPlan(c *fiber.Ctx) error {
	var req createPlanReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	plan, err := h.subscriptionsSvc.CreatePlan(c.Context(), domain.CreateSubscriptionPlanInput{
		Name:          req.Name,
		Description:   req.Description,
		DurationValue: req.DurationValue,
		DurationUnit:  req.DurationUnit,
		Price:         req.Price,
		Currency:      req.Currency,
		IsActive:      req.IsActive,
	})
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to create subscription plan",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"name": plan.Name, "price": plan.Price})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionSubscriptionPlanCreated, domain.ResourceSubscriptionPlan, &plan.ID, "Created subscription plan: "+plan.Name, meta, &ip, &ua)

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Subscription plan created successfully",
		Data:    planToRes(plan),
	})
}

// ListSubscriptionPlans godoc
// @Summary List subscription plans
// @Description Returns all subscription plans
// @Tags subscriptions
// @Produce json
// @Param active_only query bool false "Return only active plans"
// @Success 200 {object} domain.Response
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/subscription-plans [get]
func (h *Handler) ListSubscriptionPlans(c *fiber.Ctx) error {
	activeOnly := c.Query("active_only", "true") == "true"

	plans, err := h.subscriptionsSvc.ListPlans(c.Context(), activeOnly)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to list subscription plans",
			Error:   err.Error(),
		})
	}

	result := make([]planRes, len(plans))
	for i, p := range plans {
		result[i] = *planToRes(&p)
	}

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

// GetSubscriptionPlan godoc
// @Summary Get a subscription plan
// @Description Returns a single subscription plan by ID
// @Tags subscriptions
// @Produce json
// @Param id path int true "Plan ID"
// @Success 200 {object} domain.Response
// @Failure 404 {object} domain.ErrorResponse
// @Router /api/v1/subscription-plans/{id} [get]
func (h *Handler) GetSubscriptionPlan(c *fiber.Ctx) error {
	id, err := strconv.ParseInt(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid plan ID",
		})
	}

	plan, err := h.subscriptionsSvc.GetPlanByID(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "Subscription plan not found",
		})
	}

	return c.JSON(domain.Response{
		Message: "Subscription plan retrieved successfully",
		Data:    planToRes(plan),
	})
}

// UpdateSubscriptionPlan godoc
// @Summary Update a subscription plan
// @Description Updates a subscription plan (admin only)
// @Tags subscriptions
// @Accept json
// @Produce json
// @Param id path int true "Plan ID"
// @Param body body updatePlanReq true "Update plan payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/subscription-plans/{id} [put]
func (h *Handler) UpdateSubscriptionPlan(c *fiber.Ctx) error {
	id, err := strconv.ParseInt(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid plan ID",
		})
	}

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

	err = h.subscriptionsSvc.UpdatePlan(c.Context(), id, domain.UpdateSubscriptionPlanInput{
		Name:          req.Name,
		Description:   req.Description,
		DurationValue: req.DurationValue,
		DurationUnit:  req.DurationUnit,
		Price:         req.Price,
		Currency:      req.Currency,
		IsActive:      req.IsActive,
	})
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update subscription plan",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"plan_id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionSubscriptionPlanUpdated, domain.ResourceSubscriptionPlan, &id, fmt.Sprintf("Updated subscription plan ID: %d", id), meta, &ip, &ua)

	return c.JSON(domain.Response{
		Message: "Subscription plan updated successfully",
	})
}

// DeleteSubscriptionPlan godoc
// @Summary Delete a subscription plan
// @Description Deletes a subscription plan (admin only)
// @Tags subscriptions
// @Produce json
// @Param id path int true "Plan ID"
// @Success 200 {object} domain.Response
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/subscription-plans/{id} [delete]
func (h *Handler) DeleteSubscriptionPlan(c *fiber.Ctx) error {
	id, err := strconv.ParseInt(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid plan ID",
		})
	}

	err = h.subscriptionsSvc.DeletePlan(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to delete subscription plan",
			Error:   err.Error(),
		})
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"plan_id": id})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionSubscriptionPlanDeleted, domain.ResourceSubscriptionPlan, &id, fmt.Sprintf("Deleted subscription plan ID: %d", id), meta, &ip, &ua)

	return c.JSON(domain.Response{
		Message: "Subscription plan deleted successfully",
	})
}

// =====================
// User Subscription Handlers
// =====================

// Subscribe godoc
// @Summary Subscribe to a plan (Admin only - bypasses payment)
// @Description Creates a new subscription for the authenticated user. For regular users, use /payments/subscribe instead.
// @Tags subscriptions
// @Accept json
// @Produce json
// @Param body body subscribeReq true "Subscribe payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/subscriptions [post]
// @deprecated Use POST /api/v1/payments/subscribe for user subscriptions with payment
func (h *Handler) Subscribe(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	// Check role - only admins can create subscriptions without payment
	role, ok := c.Locals("role").(domain.Role)
	if !ok || (role != domain.RoleAdmin && role != domain.RoleSuperAdmin) {
		return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
			Message: "Use /api/v1/payments/subscribe to subscribe with payment",
			Error:   "Direct subscription creation requires admin privileges",
		})
	}

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

	sub, err := h.subscriptionsSvc.Subscribe(c.Context(), userID, req.PlanID, req.PaymentReference, 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 create subscription",
			Error:   err.Error(),
		})
	}

	return c.Status(fiber.StatusCreated).JSON(domain.Response{
		Message: "Subscription created successfully",
		Data:    subscriptionToRes(sub),
	})
}

// SubscribeWithPayment godoc
// @Summary Subscribe to a plan with payment
// @Description Initiates payment for a subscription plan. Returns payment URL for checkout.
// @Tags subscriptions
// @Accept json
// @Produce json
// @Param body body subscribeWithPaymentReq true "Subscribe with payment payload"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 409 {object} domain.ErrorResponse "User already has active subscription"
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/subscriptions/checkout [post]
func (h *Handler) SubscribeWithPayment(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 subscribeWithPaymentReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid request body",
			Error:   err.Error(),
		})
	}

	// Use ArifPay service to initiate payment
	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
		} else if err.Error() == "subscription plan is not active" {
			status = fiber.StatusBadRequest
		}
		return c.Status(status).JSON(domain.ErrorResponse{
			Message: "Failed to initiate subscription payment",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Payment initiated. Complete payment to activate subscription.",
		Data:    result,
	})
}

// GetMySubscription godoc
// @Summary Get current subscription
// @Description Returns the authenticated user's active subscription
// @Tags subscriptions
// @Produce json
// @Success 200 {object} domain.Response
// @Failure 404 {object} domain.ErrorResponse
// @Router /api/v1/subscriptions/me [get]
func (h *Handler) GetMySubscription(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	sub, err := h.subscriptionsSvc.GetActiveSubscription(c.Context(), userID)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
			Message: "No active subscription found",
		})
	}

	return c.JSON(domain.Response{
		Message: "Subscription retrieved successfully",
		Data:    subscriptionToRes(sub),
	})
}

// GetMySubscriptionHistory godoc
// @Summary Get subscription history
// @Description Returns the authenticated user's subscription history
// @Tags subscriptions
// @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/subscriptions/history [get]
func (h *Handler) GetMySubscriptionHistory(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"))

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

	result := make([]subscriptionRes, len(subs))
	for i, s := range subs {
		result[i] = *subscriptionToRes(&s)
	}

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

// CheckSubscriptionStatus godoc
// @Summary Check subscription status
// @Description Returns whether the authenticated user has an active subscription
// @Tags subscriptions
// @Produce json
// @Success 200 {object} domain.Response
// @Router /api/v1/subscriptions/status [get]
func (h *Handler) CheckSubscriptionStatus(c *fiber.Ctx) error {
	userID, ok := c.Locals("user_id").(int64)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
			Message: "Unauthorized",
		})
	}

	hasActive, err := h.subscriptionsSvc.HasActiveSubscription(c.Context(), userID)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to check subscription status",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Subscription status retrieved",
		Data: fiber.Map{
			"has_active_subscription": hasActive,
		},
	})
}

// CancelSubscription godoc
// @Summary Cancel subscription
// @Description Cancels the user's subscription
// @Tags subscriptions
// @Produce json
// @Param id path int true "Subscription ID"
// @Success 200 {object} domain.Response
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/subscriptions/{id}/cancel [post]
func (h *Handler) CancelSubscription(c *fiber.Ctx) error {
	id, err := strconv.ParseInt(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid subscription ID",
		})
	}

	err = h.subscriptionsSvc.CancelSubscription(c.Context(), id)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to cancel subscription",
			Error:   err.Error(),
		})
	}

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

// SetAutoRenew godoc
// @Summary Set auto-renew
// @Description Enables or disables auto-renewal for a subscription
// @Tags subscriptions
// @Accept json
// @Produce json
// @Param id path int true "Subscription ID"
// @Param body body autoRenewReq true "Auto-renew payload"
// @Success 200 {object} domain.Response
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/subscriptions/{id}/auto-renew [put]
func (h *Handler) SetAutoRenew(c *fiber.Ctx) error {
	id, err := strconv.ParseInt(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
			Message: "Invalid subscription ID",
		})
	}

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

	err = h.subscriptionsSvc.SetAutoRenew(c.Context(), id, req.AutoRenew)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
			Message: "Failed to update auto-renew setting",
			Error:   err.Error(),
		})
	}

	return c.JSON(domain.Response{
		Message: "Auto-renew setting updated successfully",
	})
}

// Helper functions

func planToRes(p *domain.SubscriptionPlan) *planRes {
	return &planRes{
		ID:            p.ID,
		Name:          p.Name,
		Description:   p.Description,
		DurationValue: p.DurationValue,
		DurationUnit:  p.DurationUnit,
		Price:         p.Price,
		Currency:      p.Currency,
		IsActive:      p.IsActive,
		CreatedAt:     p.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
	}
}

func subscriptionToRes(s *domain.UserSubscription) *subscriptionRes {
	return &subscriptionRes{
		ID:               s.ID,
		UserID:           s.UserID,
		PlanID:           s.PlanID,
		PlanName:         s.PlanName,
		StartsAt:         s.StartsAt.Format("2006-01-02T15:04:05Z07:00"),
		ExpiresAt:        s.ExpiresAt.Format("2006-01-02T15:04:05Z07:00"),
		Status:           s.Status,
		PaymentReference: s.PaymentReference,
		PaymentMethod:    s.PaymentMethod,
		AutoRenew:        s.AutoRenew,
		DurationValue:    s.DurationValue,
		DurationUnit:     s.DurationUnit,
		Price:            s.Price,
		Currency:         s.Currency,
		CreatedAt:        s.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
	}
}
