package subscriptions

import (
	"Yimaru-Backend/internal/domain"
	"Yimaru-Backend/internal/ports"
	"context"
	"errors"
	"time"
)

var (
	ErrPlanNotFound         = errors.New("subscription plan not found")
	ErrSubscriptionNotFound = errors.New("subscription not found")
	ErrAlreadySubscribed    = errors.New("user already has an active subscription")
	ErrInvalidPlan          = errors.New("invalid subscription plan")
)

type Service struct {
	store ports.SubscriptionStore
}

func NewService(store ports.SubscriptionStore) *Service {
	return &Service{store: store}
}

// =====================
// Subscription Plans
// =====================

func (s *Service) CreatePlan(ctx context.Context, input domain.CreateSubscriptionPlanInput) (*domain.SubscriptionPlan, error) {
	return s.store.CreateSubscriptionPlan(ctx, input)
}

func (s *Service) GetPlanByID(ctx context.Context, id int64) (*domain.SubscriptionPlan, error) {
	return s.store.GetSubscriptionPlanByID(ctx, id)
}

func (s *Service) ListPlans(ctx context.Context, activeOnly bool) ([]domain.SubscriptionPlan, error) {
	return s.store.ListSubscriptionPlans(ctx, activeOnly)
}

func (s *Service) UpdatePlan(ctx context.Context, id int64, input domain.UpdateSubscriptionPlanInput) error {
	return s.store.UpdateSubscriptionPlan(ctx, id, input)
}

func (s *Service) DeletePlan(ctx context.Context, id int64) error {
	return s.store.DeleteSubscriptionPlan(ctx, id)
}

// =====================
// User Subscriptions
// =====================

// Subscribe creates a new subscription for a user
func (s *Service) Subscribe(ctx context.Context, userID, planID int64, paymentRef, paymentMethod *string) (*domain.UserSubscription, error) {
	// Check if user already has an active subscription
	hasActive, err := s.store.HasActiveSubscription(ctx, userID)
	if err != nil {
		return nil, err
	}
	if hasActive {
		return nil, ErrAlreadySubscribed
	}

	// Get the plan to calculate expiry
	plan, err := s.store.GetSubscriptionPlanByID(ctx, planID)
	if err != nil {
		return nil, ErrPlanNotFound
	}
	if !plan.IsActive {
		return nil, ErrInvalidPlan
	}

	// Calculate expiry date
	startsAt := time.Now()
	expiresAt := domain.CalculateExpiryDate(startsAt, plan.DurationValue, plan.DurationUnit)

	input := domain.CreateUserSubscriptionInput{
		UserID:           userID,
		PlanID:           planID,
		StartsAt:         &startsAt,
		ExpiresAt:        expiresAt,
		Status:           strPtr(string(domain.SubscriptionStatusActive)),
		PaymentReference: paymentRef,
		PaymentMethod:    paymentMethod,
		AutoRenew:        boolPtr(false),
	}

	return s.store.CreateUserSubscription(ctx, input)
}

func (s *Service) GetSubscriptionByID(ctx context.Context, id int64) (*domain.UserSubscription, error) {
	return s.store.GetUserSubscriptionByID(ctx, id)
}

func (s *Service) GetActiveSubscription(ctx context.Context, userID int64) (*domain.UserSubscription, error) {
	return s.store.GetActiveSubscriptionByUserID(ctx, userID)
}

func (s *Service) GetSubscriptionHistory(ctx context.Context, userID int64, limit, offset int32) ([]domain.UserSubscription, error) {
	return s.store.GetUserSubscriptionHistory(ctx, userID, limit, offset)
}

func (s *Service) HasActiveSubscription(ctx context.Context, userID int64) (bool, error) {
	return s.store.HasActiveSubscription(ctx, userID)
}

func (s *Service) CancelSubscription(ctx context.Context, subscriptionID int64) error {
	return s.store.CancelUserSubscription(ctx, subscriptionID)
}

func (s *Service) SetAutoRenew(ctx context.Context, subscriptionID int64, autoRenew bool) error {
	return s.store.UpdateAutoRenew(ctx, subscriptionID, autoRenew)
}

// RenewSubscription extends an existing subscription
func (s *Service) RenewSubscription(ctx context.Context, subscriptionID int64) (*domain.UserSubscription, error) {
	sub, err := s.store.GetUserSubscriptionByID(ctx, subscriptionID)
	if err != nil {
		return nil, ErrSubscriptionNotFound
	}

	plan, err := s.store.GetSubscriptionPlanByID(ctx, sub.PlanID)
	if err != nil {
		return nil, ErrPlanNotFound
	}

	// Calculate new expiry from current expiry (or now if expired)
	baseTime := sub.ExpiresAt
	if baseTime.Before(time.Now()) {
		baseTime = time.Now()
	}
	newExpiry := domain.CalculateExpiryDate(baseTime, plan.DurationValue, plan.DurationUnit)

	err = s.store.ExtendSubscription(ctx, subscriptionID, newExpiry)
	if err != nil {
		return nil, err
	}

	// Also reactivate if expired
	if sub.Status == string(domain.SubscriptionStatusExpired) {
		err = s.store.UpdateSubscriptionStatus(ctx, subscriptionID, string(domain.SubscriptionStatusActive))
		if err != nil {
			return nil, err
		}
	}

	return s.store.GetUserSubscriptionByID(ctx, subscriptionID)
}

// Helper functions
func strPtr(s string) *string {
	return &s
}

func boolPtr(b bool) *bool {
	return &b
}
