package portal

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"

	"github.com/stripe/stripe-go/v75"
	"github.com/stripe/stripe-go/v75/webhook"
)

func parseCPU(cpu string) (int64, error) {
	if cpu == "" {
		return 0, nil
	}
	// Basic implementation - assumes millicores
	value, err := strconv.ParseInt(strings.TrimSuffix(cpu, "m"), 10, 64)
	if err != nil {
		return 0, err
	}
	return value, nil
}

func parseMemory(memory string) (int64, error) {
	if memory == "" {
		return 0, nil
	}

	memory = strings.ToLower(memory)
	multiplier := int64(1)

	if strings.HasSuffix(memory, "ki") {
		multiplier = 1024
		memory = memory[:len(memory)-2]
	} else if strings.HasSuffix(memory, "mi") {
		multiplier = 1024 * 1024
		memory = memory[:len(memory)-2]
	} else if strings.HasSuffix(memory, "gi") {
		multiplier = 1024 * 1024 * 1024
		memory = memory[:len(memory)-2]
	}

	value, err := strconv.ParseInt(memory, 10, 64)
	if err != nil {
		return 0, err
	}

	return value * multiplier, nil
}

type BillingPlan struct {
	Name           string `json:"name"`
	Price          int    `json:"price"`
	MaxDeployments int    `json:"maxDeployments"`
	MaxCPU         string `json:"maxCPU"`
	MaxMemory      string `json:"maxMemory"`
	MaxClusters    int    `json:"maxClusters"`
}

type ResourceUsage struct {
	Deployments          int     `json:"deployments"`
	DeploymentPercentage float64 `json:"deploymentPercentage"`
	CPU                  string  `json:"cpu"`
	CPUPercentage        float64 `json:"cpuPercentage"`
	Memory               string  `json:"memory"`
	MemoryPercentage     float64 `json:"memoryPercentage"`
	Clusters             int     `json:"clusters"`
	ClusterPercentage    float64 `json:"clusterPercentage"`
}

type BillingTransaction struct {
	Date        string  `json:"date"`
	Description string  `json:"description"`
	Amount      float64 `json:"amount"`
	Status      string  `json:"status"`
}

type PricingModel struct {
	UsageBased struct {
		BasePrice    float64         `json:"basePrice"`
		Resources    ResourcePricing `json:"resources"`
		HAMultiplier float64         `json:"haMultiplier"`
	} `json:"usageBased"`
	Promos map[string]PromoPackage `json:"promos"`
}

type ResourcePricing struct {
	CPU     float64 `json:"cpu"`
	Memory  float64 `json:"memory"`
	Storage float64 `json:"storage"`
	Network float64 `json:"network"`
}

type PromoPackage struct {
	Price       float64        `json:"price"`
	Description string         `json:"description"`
	Limits      ResourceLimits `json:"limits"`
}

type ResourceLimits struct {
	Deployments int    `json:"deployments"`
	CPU         string `json:"cpu"`
	Memory      string `json:"memory"`
	Storage     string `json:"storage"`
	Clusters    int    `json:"clusters"`
}

type BillingInfo struct {
	CurrentUsage  ResourceUsage `json:"currentUsage"`
	CurrentPromo  *string       `json:"currentPromo,omitempty"`
	EstimatedCost float64       `json:"estimatedCost"`
	PromoEligible []string      `json:"promoEligible"`
}

func (p *Portal) handleBilling(w http.ResponseWriter, r *http.Request) {
	// Get current customer
	customer, err := p.operator.GetCurrentCustomer(r.Context())
	if err != nil {
		http.Error(w, "Failed to get customer info", http.StatusInternalServerError)
		return
	}

	// Get resource usage
	usage, err := p.operator.GetResourceUsage(r.Context(), customer.Name)
	if err != nil {
		http.Error(w, "Failed to get resource usage", http.StatusInternalServerError)
		return
	}

	// Get billing history
	history, err := p.operator.GetBillingHistory(r.Context(), customer.Name)
	if err != nil {
		http.Error(w, "Failed to get billing history", http.StatusInternalServerError)
		return
	}

	// Check promo eligibility
	promos := p.getPricingConfig().Promos
	var eligiblePromos []string
	for name, promo := range promos {
		if isEligibleForPromo(usage, promo.Limits) {
			eligiblePromos = append(eligiblePromos, name)
		}
	}

	data := struct {
		Usage          ResourceUsage        `json:"usage"`
		Pricing        PricingModel         `json:"pricing"`
		BillingHistory []BillingTransaction `json:"billingHistory"`
		PromoEligible  []string             `json:"promoEligible"`
	}{
		Usage:          usage,
		Pricing:        p.getPricingConfig(),
		BillingHistory: history,
		PromoEligible:  eligiblePromos,
	}

	if err := p.templates.ExecuteTemplate(w, "billing.html", data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (p *Portal) handleAPIBillingPlan(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req struct {
		Plan string `json:"plan"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	if err := p.operator.UpdateBillingPlan(r.Context(), req.Plan); err != nil {
		http.Error(w, "Failed to update plan", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func getAvailablePlans() []BillingPlan {
	return []BillingPlan{
		{
			Name:           "basic",
			Price:          49,
			MaxDeployments: 3,
			MaxCPU:         "2",
			MaxMemory:      "2Gi",
			MaxClusters:    1,
		},
		{
			Name:           "pro",
			Price:          149,
			MaxDeployments: 10,
			MaxCPU:         "8",
			MaxMemory:      "8Gi",
			MaxClusters:    3,
		},
		{
			Name:           "enterprise",
			Price:          499,
			MaxDeployments: 50,
			MaxCPU:         "32",
			MaxMemory:      "64Gi",
			MaxClusters:    10,
		},
	}
}

func getPlanPrice(plan string) int {
	prices := map[string]int{
		"basic":      49,
		"pro":        149,
		"enterprise": 499,
	}
	return prices[plan]
}

func isEligibleForPromo(usage ResourceUsage, limits ResourceLimits) bool {
	// Check if current usage is within promo limits
	if usage.Deployments > limits.Deployments {
		return false
	}

	cpuUsage, _ := parseCPU(usage.CPU)
	cpuLimit, _ := parseCPU(limits.CPU)
	if cpuUsage > cpuLimit {
		return false
	}

	memUsage, _ := parseMemory(usage.Memory)
	memLimit, _ := parseMemory(limits.Memory)
	if memUsage > memLimit {
		return false
	}

	// ... additional checks ...

	return true
}

func (p *Portal) handleStripeWebhook(w http.ResponseWriter, r *http.Request) {
	const MaxBodyBytes = int64(65536)
	r.Body = http.MaxBytesReader(w, r.Body, MaxBodyBytes)
	payload, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error reading request body: %v", err), http.StatusServiceUnavailable)
		return
	}

	// Verify webhook signature
	event, err := webhook.ConstructEvent(
		payload,
		r.Header.Get("Stripe-Signature"),
		p.stripeWebhookSecret,
	)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error verifying webhook signature: %v", err), http.StatusBadRequest)
		return
	}

	switch event.Type {
	case "invoice.paid":
		var invoice stripe.Invoice
		err := json.Unmarshal(event.Data.Raw, &invoice)
		if err != nil {
			http.Error(w, fmt.Sprintf("Error parsing webhook JSON: %v", err), http.StatusBadRequest)
			return
		}
		// Update customer billing status
		if err := p.operator.UpdateBillingStatus(r.Context(), invoice.Customer.ID, "active"); err != nil {
			log.Printf("Error updating billing status: %v", err)
		}

	case "invoice.payment_failed":
		var invoice stripe.Invoice
		err := json.Unmarshal(event.Data.Raw, &invoice)
		if err != nil {
			http.Error(w, fmt.Sprintf("Error parsing webhook JSON: %v", err), http.StatusBadRequest)
			return
		}
		// Update customer billing status and possibly restrict resources
		if err := p.operator.UpdateBillingStatus(r.Context(), invoice.Customer.ID, "past_due"); err != nil {
			log.Printf("Error updating billing status: %v", err)
		}
	}

	w.WriteHeader(http.StatusOK)
}

func (p *Portal) getPricingConfig() (*config.PricingConfig, error) {
	// Implementation depends on your configuration system
	return nil, fmt.Errorf("getPricingConfig not implemented")
}
