package handlers

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

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

func init() {
	// Set Gin to test mode
	gin.SetMode(gin.TestMode)
}

func TestGetPlans_ResponseFormat(t *testing.T) {
	// Test that GetPlans returns the expected response structure
	// Since we don't have a real database connection in unit tests,
	// we test the expected structure and format

	// Test that default plans are well-formed
	plans := models.GetDefaultPlans()

	if len(plans) == 0 {
		t.Error("Default plans should not be empty")
	}

	for _, plan := range plans {
		// Verify JSON serialization works
		data, err := json.Marshal(plan)
		if err != nil {
			t.Errorf("Failed to marshal plan %s: %v", plan.ID, err)
		}

		// Verify we can unmarshal back
		var decoded models.BillingPlan
		err = json.Unmarshal(data, &decoded)
		if err != nil {
			t.Errorf("Failed to unmarshal plan %s: %v", plan.ID, err)
		}

		// Verify key fields
		if decoded.ID != plan.ID {
			t.Errorf("Plan ID mismatch: got %s, expected %s", decoded.ID, plan.ID)
		}
	}
}

func TestBillingStatusResponse_Structure(t *testing.T) {
	// Test the response structure
	response := models.BillingStatusResponse{
		HasSubscription: true,
		Subscription: &models.SubscriptionInfo{
			PlanID:            models.PlanStandard,
			PlanName:          "Standard",
			Status:            models.StatusActive,
			IsTrialing:        false,
			CancelAtPeriodEnd: false,
			PriceCents:        1990,
			Currency:          "eur",
		},
		TaskUsage: &models.TaskUsageInfo{
			TasksAvailable: 85,
			MaxTasks:       500,
			InfoText:       "Aufgaben verfuegbar: 85 von max. 500",
			TooltipText:    "Aufgaben koennen sich bis zu 5 Monate ansammeln.",
		},
		Entitlements: &models.EntitlementInfo{
			Features:        []string{"basic_ai", "basic_documents", "templates", "batch_processing"},
			MaxTeamMembers:  3,
			PrioritySupport: false,
			CustomBranding:  false,
			BatchProcessing: true,
			CustomTemplates: true,
			FairUseMode:     false,
		},
		AvailablePlans: models.GetDefaultPlans(),
	}

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

	// Verify it's valid JSON
	var decoded map[string]interface{}
	err = json.Unmarshal(data, &decoded)
	if err != nil {
		t.Fatalf("Response is not valid JSON: %v", err)
	}

	// Check required fields exist
	if _, ok := decoded["has_subscription"]; !ok {
		t.Error("Response should have 'has_subscription' field")
	}
}

func TestStartTrialRequest_Validation(t *testing.T) {
	tests := []struct {
		name      string
		request   models.StartTrialRequest
		wantError bool
	}{
		{
			name:      "Valid basic plan",
			request:   models.StartTrialRequest{PlanID: models.PlanBasic},
			wantError: false,
		},
		{
			name:      "Valid standard plan",
			request:   models.StartTrialRequest{PlanID: models.PlanStandard},
			wantError: false,
		},
		{
			name:      "Valid premium plan",
			request:   models.StartTrialRequest{PlanID: models.PlanPremium},
			wantError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Test JSON serialization
			data, err := json.Marshal(tt.request)
			if err != nil {
				t.Fatalf("Failed to marshal request: %v", err)
			}

			var decoded models.StartTrialRequest
			err = json.Unmarshal(data, &decoded)
			if err != nil {
				t.Fatalf("Failed to unmarshal request: %v", err)
			}

			if decoded.PlanID != tt.request.PlanID {
				t.Errorf("PlanID mismatch: got %s, expected %s", decoded.PlanID, tt.request.PlanID)
			}
		})
	}
}

func TestChangePlanRequest_Structure(t *testing.T) {
	request := models.ChangePlanRequest{
		NewPlanID: models.PlanPremium,
	}

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

	var decoded map[string]interface{}
	err = json.Unmarshal(data, &decoded)
	if err != nil {
		t.Fatalf("Response is not valid JSON: %v", err)
	}

	if _, ok := decoded["new_plan_id"]; !ok {
		t.Error("Request should have 'new_plan_id' field")
	}
}

func TestStartTrialResponse_Structure(t *testing.T) {
	response := models.StartTrialResponse{
		CheckoutURL: "https://checkout.stripe.com/c/pay/cs_test_123",
		SessionID:   "cs_test_123",
	}

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

	var decoded map[string]interface{}
	err = json.Unmarshal(data, &decoded)
	if err != nil {
		t.Fatalf("Response is not valid JSON: %v", err)
	}

	if _, ok := decoded["checkout_url"]; !ok {
		t.Error("Response should have 'checkout_url' field")
	}
	if _, ok := decoded["session_id"]; !ok {
		t.Error("Response should have 'session_id' field")
	}
}

func TestCancelSubscriptionResponse_Structure(t *testing.T) {
	response := models.CancelSubscriptionResponse{
		Success:     true,
		Message:     "Subscription will be canceled at the end of the billing period",
		CancelDate:  "2025-01-16",
		ActiveUntil: "2025-01-16",
	}

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

	if !response.Success {
		t.Error("Success should be true")
	}
}

func TestCustomerPortalResponse_Structure(t *testing.T) {
	response := models.CustomerPortalResponse{
		PortalURL: "https://billing.stripe.com/p/session/test_123",
	}

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

	var decoded map[string]interface{}
	err = json.Unmarshal(data, &decoded)
	if err != nil {
		t.Fatalf("Response is not valid JSON: %v", err)
	}

	if _, ok := decoded["portal_url"]; !ok {
		t.Error("Response should have 'portal_url' field")
	}
}

func TestEntitlementCheckResponse_Structure(t *testing.T) {
	tests := []struct {
		name     string
		response models.EntitlementCheckResponse
	}{
		{
			name: "Has entitlement",
			response: models.EntitlementCheckResponse{
				HasEntitlement: true,
				PlanID:         models.PlanStandard,
			},
		},
		{
			name: "No entitlement",
			response: models.EntitlementCheckResponse{
				HasEntitlement: false,
				PlanID:         models.PlanBasic,
				Message:        "Feature not available in this plan",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			data, err := json.Marshal(tt.response)
			if err != nil {
				t.Fatalf("Failed to marshal EntitlementCheckResponse: %v", err)
			}

			var decoded map[string]interface{}
			err = json.Unmarshal(data, &decoded)
			if err != nil {
				t.Fatalf("Response is not valid JSON: %v", err)
			}

			if _, ok := decoded["has_entitlement"]; !ok {
				t.Error("Response should have 'has_entitlement' field")
			}
		})
	}
}

func TestTrackUsageRequest_Validation(t *testing.T) {
	tests := []struct {
		name    string
		request models.TrackUsageRequest
		valid   bool
	}{
		{
			name: "Valid AI request",
			request: models.TrackUsageRequest{
				UserID:    "550e8400-e29b-41d4-a716-446655440000",
				UsageType: "ai_request",
				Quantity:  1,
			},
			valid: true,
		},
		{
			name: "Valid document created",
			request: models.TrackUsageRequest{
				UserID:    "550e8400-e29b-41d4-a716-446655440000",
				UsageType: "document_created",
				Quantity:  1,
			},
			valid: true,
		},
		{
			name: "Multiple quantity",
			request: models.TrackUsageRequest{
				UserID:    "550e8400-e29b-41d4-a716-446655440000",
				UsageType: "ai_request",
				Quantity:  5,
			},
			valid: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			data, err := json.Marshal(tt.request)
			if err != nil {
				t.Fatalf("Failed to marshal TrackUsageRequest: %v", err)
			}

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

			if decoded.UserID != tt.request.UserID {
				t.Errorf("UserID mismatch: got %s, expected %s", decoded.UserID, tt.request.UserID)
			}
		})
	}
}

func TestCheckUsageResponse_Format(t *testing.T) {
	tests := []struct {
		name     string
		response models.CheckUsageResponse
	}{
		{
			name: "Allowed response",
			response: models.CheckUsageResponse{
				Allowed:      true,
				CurrentUsage: 450,
				Limit:        1500,
				Remaining:    1050,
			},
		},
		{
			name: "Limit reached",
			response: models.CheckUsageResponse{
				Allowed:      false,
				CurrentUsage: 1500,
				Limit:        1500,
				Remaining:    0,
				Message:      "Usage limit reached for ai_request (1500/1500)",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			data, err := json.Marshal(tt.response)
			if err != nil {
				t.Fatalf("Failed to marshal CheckUsageResponse: %v", err)
			}

			var decoded map[string]interface{}
			err = json.Unmarshal(data, &decoded)
			if err != nil {
				t.Fatalf("Response is not valid JSON: %v", err)
			}

			if _, ok := decoded["allowed"]; !ok {
				t.Error("Response should have 'allowed' field")
			}
		})
	}
}

func TestConsumeTaskRequest_Format(t *testing.T) {
	tests := []struct {
		name    string
		request models.ConsumeTaskRequest
	}{
		{
			name: "Correction task",
			request: models.ConsumeTaskRequest{
				UserID:   "550e8400-e29b-41d4-a716-446655440000",
				TaskType: models.TaskTypeCorrection,
			},
		},
		{
			name: "Letter task",
			request: models.ConsumeTaskRequest{
				UserID:   "550e8400-e29b-41d4-a716-446655440000",
				TaskType: models.TaskTypeLetter,
			},
		},
		{
			name: "Batch task",
			request: models.ConsumeTaskRequest{
				UserID:   "550e8400-e29b-41d4-a716-446655440000",
				TaskType: models.TaskTypeBatch,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			data, err := json.Marshal(tt.request)
			if err != nil {
				t.Fatalf("Failed to marshal ConsumeTaskRequest: %v", err)
			}

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

			if decoded.TaskType != tt.request.TaskType {
				t.Errorf("TaskType mismatch: got %s, expected %s", decoded.TaskType, tt.request.TaskType)
			}
		})
	}
}

func TestConsumeTaskResponse_Format(t *testing.T) {
	tests := []struct {
		name     string
		response models.ConsumeTaskResponse
	}{
		{
			name: "Successful consumption",
			response: models.ConsumeTaskResponse{
				Success:        true,
				TaskID:         "task-uuid-123",
				TasksRemaining: 49,
			},
		},
		{
			name: "Limit reached",
			response: models.ConsumeTaskResponse{
				Success:        false,
				TasksRemaining: 0,
				Message:        "Dein Aufgaben-Kontingent ist aufgebraucht.",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			data, err := json.Marshal(tt.response)
			if err != nil {
				t.Fatalf("Failed to marshal ConsumeTaskResponse: %v", err)
			}

			var decoded map[string]interface{}
			err = json.Unmarshal(data, &decoded)
			if err != nil {
				t.Fatalf("Response is not valid JSON: %v", err)
			}

			if _, ok := decoded["success"]; !ok {
				t.Error("Response should have 'success' field")
			}
			if _, ok := decoded["tasks_remaining"]; !ok {
				t.Error("Response should have 'tasks_remaining' field")
			}
		})
	}
}

func TestCheckTaskAllowedResponse_Format(t *testing.T) {
	tests := []struct {
		name     string
		response models.CheckTaskAllowedResponse
	}{
		{
			name: "Task allowed",
			response: models.CheckTaskAllowedResponse{
				Allowed:        true,
				TasksAvailable: 50,
				MaxTasks:       150,
				PlanID:         models.PlanBasic,
			},
		},
		{
			name: "Task not allowed",
			response: models.CheckTaskAllowedResponse{
				Allowed:        false,
				TasksAvailable: 0,
				MaxTasks:       150,
				PlanID:         models.PlanBasic,
				Message:        "Dein Aufgaben-Kontingent ist aufgebraucht.",
			},
		},
		{
			name: "Premium Fair Use",
			response: models.CheckTaskAllowedResponse{
				Allowed:        true,
				TasksAvailable: 1000,
				MaxTasks:       5000,
				PlanID:         models.PlanPremium,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			data, err := json.Marshal(tt.response)
			if err != nil {
				t.Fatalf("Failed to marshal CheckTaskAllowedResponse: %v", err)
			}

			var decoded map[string]interface{}
			err = json.Unmarshal(data, &decoded)
			if err != nil {
				t.Fatalf("Response is not valid JSON: %v", err)
			}

			if _, ok := decoded["allowed"]; !ok {
				t.Error("Response should have 'allowed' field")
			}
			if _, ok := decoded["tasks_available"]; !ok {
				t.Error("Response should have 'tasks_available' field")
			}
			if _, ok := decoded["plan_id"]; !ok {
				t.Error("Response should have 'plan_id' field")
			}
		})
	}
}

// HTTP Handler Tests (without DB)

func TestHTTPErrorResponse_Format(t *testing.T) {
	// Test standard error response format
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	// Simulate an error response
	c.JSON(http.StatusUnauthorized, gin.H{
		"error":   "unauthorized",
		"message": "User not authenticated",
	})

	if w.Code != http.StatusUnauthorized {
		t.Errorf("Expected status 401, got %d", w.Code)
	}

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	if err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	if _, ok := response["error"]; !ok {
		t.Error("Error response should have 'error' field")
	}
	if _, ok := response["message"]; !ok {
		t.Error("Error response should have 'message' field")
	}
}

func TestHTTPSuccessResponse_Format(t *testing.T) {
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	// Simulate a success response
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Operation completed",
	})

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	if err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	if response["success"] != true {
		t.Error("Success response should have success=true")
	}
}

func TestRequestParsing_InvalidJSON(t *testing.T) {
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	// Create request with invalid JSON
	invalidJSON := []byte(`{"plan_id": }`) // Invalid JSON
	c.Request = httptest.NewRequest("POST", "/test", bytes.NewReader(invalidJSON))
	c.Request.Header.Set("Content-Type", "application/json")

	var req models.StartTrialRequest
	err := c.ShouldBindJSON(&req)

	if err == nil {
		t.Error("Should return error for invalid JSON")
	}
}

func TestHTTPHeaders_ContentType(t *testing.T) {
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

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

	contentType := w.Header().Get("Content-Type")
	if contentType != "application/json; charset=utf-8" {
		t.Errorf("Expected JSON content type, got %s", contentType)
	}
}
