package services

import (
	"encoding/json"
	"testing"

	"github.com/breakpilot/billing-service/internal/models"
)

func TestSubscriptionStatus_Transitions(t *testing.T) {
	// Test valid subscription status values
	validStatuses := []models.SubscriptionStatus{
		models.StatusTrialing,
		models.StatusActive,
		models.StatusPastDue,
		models.StatusCanceled,
		models.StatusExpired,
	}

	for _, status := range validStatuses {
		if status == "" {
			t.Errorf("Status should not be empty")
		}
	}
}

func TestPlanID_ValidValues(t *testing.T) {
	validPlanIDs := []models.PlanID{
		models.PlanBasic,
		models.PlanStandard,
		models.PlanPremium,
	}

	expected := []string{"basic", "standard", "premium"}

	for i, planID := range validPlanIDs {
		if string(planID) != expected[i] {
			t.Errorf("PlanID should be '%s', got '%s'", expected[i], planID)
		}
	}
}

func TestPlanFeatures_JSONSerialization(t *testing.T) {
	features := models.PlanFeatures{
		MonthlyTaskAllowance: 100,
		MaxTaskBalance:       500,
		FeatureFlags:         []string{"basic_ai", "templates"},
		MaxTeamMembers:       3,
		PrioritySupport:      false,
		CustomBranding:       false,
		BatchProcessing:      true,
		CustomTemplates:      true,
		FairUseMode:          false,
	}

	// Test JSON serialization
	data, err := json.Marshal(features)
	if err != nil {
		t.Fatalf("Failed to marshal PlanFeatures: %v", err)
	}

	// Test JSON deserialization
	var decoded models.PlanFeatures
	err = json.Unmarshal(data, &decoded)
	if err != nil {
		t.Fatalf("Failed to unmarshal PlanFeatures: %v", err)
	}

	// Verify fields
	if decoded.MonthlyTaskAllowance != features.MonthlyTaskAllowance {
		t.Errorf("MonthlyTaskAllowance mismatch: got %d, expected %d",
			decoded.MonthlyTaskAllowance, features.MonthlyTaskAllowance)
	}
	if decoded.MaxTaskBalance != features.MaxTaskBalance {
		t.Errorf("MaxTaskBalance mismatch: got %d, expected %d",
			decoded.MaxTaskBalance, features.MaxTaskBalance)
	}
	if decoded.BatchProcessing != features.BatchProcessing {
		t.Errorf("BatchProcessing mismatch: got %v, expected %v",
			decoded.BatchProcessing, features.BatchProcessing)
	}
}

func TestBillingPlan_DefaultPlansAreValid(t *testing.T) {
	plans := models.GetDefaultPlans()

	if len(plans) != 3 {
		t.Fatalf("Expected 3 default plans, got %d", len(plans))
	}

	// Verify all plans have required fields
	for _, plan := range plans {
		if plan.ID == "" {
			t.Errorf("Plan ID should not be empty")
		}
		if plan.Name == "" {
			t.Errorf("Plan '%s' should have a name", plan.ID)
		}
		if plan.Description == "" {
			t.Errorf("Plan '%s' should have a description", plan.ID)
		}
		if plan.PriceCents <= 0 {
			t.Errorf("Plan '%s' should have a positive price, got %d", plan.ID, plan.PriceCents)
		}
		if plan.Currency != "eur" {
			t.Errorf("Plan '%s' currency should be 'eur', got '%s'", plan.ID, plan.Currency)
		}
		if plan.Interval != "month" {
			t.Errorf("Plan '%s' interval should be 'month', got '%s'", plan.ID, plan.Interval)
		}
		if !plan.IsActive {
			t.Errorf("Plan '%s' should be active", plan.ID)
		}
		if plan.SortOrder <= 0 {
			t.Errorf("Plan '%s' should have a positive sort order, got %d", plan.ID, plan.SortOrder)
		}
	}
}

func TestBillingPlan_TaskAllowanceProgression(t *testing.T) {
	plans := models.GetDefaultPlans()

	// Basic should have lowest allowance
	basic := plans[0]
	standard := plans[1]
	premium := plans[2]

	if basic.Features.MonthlyTaskAllowance >= standard.Features.MonthlyTaskAllowance {
		t.Error("Standard plan should have more tasks than Basic")
	}

	if standard.Features.MonthlyTaskAllowance >= premium.Features.MonthlyTaskAllowance {
		t.Error("Premium plan should have more tasks than Standard")
	}
}

func TestBillingPlan_PriceProgression(t *testing.T) {
	plans := models.GetDefaultPlans()

	// Prices should increase with each tier
	if plans[0].PriceCents >= plans[1].PriceCents {
		t.Error("Standard should cost more than Basic")
	}
	if plans[1].PriceCents >= plans[2].PriceCents {
		t.Error("Premium should cost more than Standard")
	}
}

func TestBillingPlan_FairUseModeOnlyForPremium(t *testing.T) {
	plans := models.GetDefaultPlans()

	for _, plan := range plans {
		if plan.ID == models.PlanPremium {
			if !plan.Features.FairUseMode {
				t.Error("Premium plan should have FairUseMode enabled")
			}
		} else {
			if plan.Features.FairUseMode {
				t.Errorf("Plan '%s' should not have FairUseMode enabled", plan.ID)
			}
		}
	}
}

func TestBillingPlan_MaxTaskBalanceCalculation(t *testing.T) {
	plans := models.GetDefaultPlans()

	for _, plan := range plans {
		expected := plan.Features.MonthlyTaskAllowance * models.CarryoverMonthsCap
		if plan.Features.MaxTaskBalance != expected {
			t.Errorf("Plan '%s' MaxTaskBalance should be %d (allowance * 5), got %d",
				plan.ID, expected, plan.Features.MaxTaskBalance)
		}
	}
}

func TestAuditLogJSON_Marshaling(t *testing.T) {
	// Test that audit log values can be properly serialized
	oldValue := map[string]interface{}{
		"plan_id": "basic",
		"status":  "active",
	}

	newValue := map[string]interface{}{
		"plan_id": "standard",
		"status":  "active",
	}

	metadata := map[string]interface{}{
		"reason": "upgrade",
	}

	// Marshal all values
	oldJSON, err := json.Marshal(oldValue)
	if err != nil {
		t.Fatalf("Failed to marshal oldValue: %v", err)
	}

	newJSON, err := json.Marshal(newValue)
	if err != nil {
		t.Fatalf("Failed to marshal newValue: %v", err)
	}

	metaJSON, err := json.Marshal(metadata)
	if err != nil {
		t.Fatalf("Failed to marshal metadata: %v", err)
	}

	// Verify non-empty
	if len(oldJSON) == 0 || len(newJSON) == 0 || len(metaJSON) == 0 {
		t.Error("JSON outputs should not be empty")
	}
}

func TestSubscriptionTrialCalculation(t *testing.T) {
	// Test trial days calculation logic
	trialDays := 7

	if trialDays <= 0 {
		t.Error("Trial days should be positive")
	}

	if trialDays > 30 {
		t.Error("Trial days should not exceed 30")
	}
}

func TestSubscriptionInfo_TrialingStatus(t *testing.T) {
	info := models.SubscriptionInfo{
		PlanID:            models.PlanBasic,
		PlanName:          "Basic",
		Status:            models.StatusTrialing,
		IsTrialing:        true,
		TrialDaysLeft:     5,
		CancelAtPeriodEnd: false,
		PriceCents:        990,
		Currency:          "eur",
	}

	if !info.IsTrialing {
		t.Error("Should be trialing")
	}
	if info.Status != models.StatusTrialing {
		t.Errorf("Status should be 'trialing', got '%s'", info.Status)
	}
	if info.TrialDaysLeft <= 0 {
		t.Error("TrialDaysLeft should be positive during trial")
	}
}

func TestSubscriptionInfo_ActiveStatus(t *testing.T) {
	info := models.SubscriptionInfo{
		PlanID:            models.PlanStandard,
		PlanName:          "Standard",
		Status:            models.StatusActive,
		IsTrialing:        false,
		TrialDaysLeft:     0,
		CancelAtPeriodEnd: false,
		PriceCents:        1990,
		Currency:          "eur",
	}

	if info.IsTrialing {
		t.Error("Should not be trialing")
	}
	if info.Status != models.StatusActive {
		t.Errorf("Status should be 'active', got '%s'", info.Status)
	}
}

func TestSubscriptionInfo_CanceledStatus(t *testing.T) {
	info := models.SubscriptionInfo{
		PlanID:            models.PlanStandard,
		PlanName:          "Standard",
		Status:            models.StatusActive,
		IsTrialing:        false,
		CancelAtPeriodEnd: true, // Scheduled for cancellation
		PriceCents:        1990,
		Currency:          "eur",
	}

	if !info.CancelAtPeriodEnd {
		t.Error("CancelAtPeriodEnd should be true")
	}
	// Status remains active until period end
	if info.Status != models.StatusActive {
		t.Errorf("Status should still be 'active', got '%s'", info.Status)
	}
}

func TestWebhookEventTypes(t *testing.T) {
	// Test common Stripe webhook event types we handle
	eventTypes := []string{
		"checkout.session.completed",
		"customer.subscription.created",
		"customer.subscription.updated",
		"customer.subscription.deleted",
		"invoice.paid",
		"invoice.payment_failed",
	}

	for _, eventType := range eventTypes {
		if eventType == "" {
			t.Error("Event type should not be empty")
		}
	}
}

func TestIdempotencyKey_Format(t *testing.T) {
	// Test that we can handle Stripe event IDs
	sampleEventIDs := []string{
		"evt_1234567890abcdef",
		"evt_test_abc123xyz789",
		"evt_live_real_event_id",
	}

	for _, eventID := range sampleEventIDs {
		if len(eventID) < 10 {
			t.Errorf("Event ID '%s' seems too short", eventID)
		}
		// Stripe event IDs typically start with "evt_"
		if eventID[:4] != "evt_" {
			t.Errorf("Event ID '%s' should start with 'evt_'", eventID)
		}
	}
}
