package portal

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

	"github.com/pfeifferj/container-mom-go/pkg/apis/containermom/v1alpha1"
	"github.com/pfeifferj/container-mom-go/pkg/billing"
	"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
}

// BillingPlan represents a subscription tier with its features and limits
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"`
}

// ResourceUsage tracks resource consumption metrics
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"`
}

// BillingTransaction represents a billing event
type BillingTransaction struct {
	Date        string  `json:"date"`
	Description string  `json:"description"`
	Amount      float64 `json:"amount"`
	Status      string  `json:"status"`
}

// PricingModel defines the pricing structure for resources
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"`
}

// ResourcePricing defines the cost structure for a resource type
type ResourcePricing struct {
	CPU     float64 `json:"cpu"`
	Memory  float64 `json:"memory"`
	Storage float64 `json:"storage"`
	Network float64 `json:"network"`
}

// PromoPackage represents a promotional offering
type PromoPackage struct {
	Price       float64        `json:"price"`
	Description string         `json:"description"`
	Limits      ResourceLimits `json:"limits"`
}

// ResourceLimits defines the maximum allowed resource usage
type ResourceLimits struct {
	Deployments int    `json:"deployments"`
	CPU         string `json:"cpu"`
	Memory      string `json:"memory"`
	Storage     string `json:"storage"`
	Clusters    int    `json:"clusters"`
}

// BillingInfo contains billing-related information for display
type BillingInfo struct {
	CurrentUsage  ResourceUsage `json:"currentUsage"`
	CurrentPromo  *string       `json:"currentPromo,omitempty"`
	EstimatedCost float64       `json:"estimatedCost"`
	PromoEligible []string      `json:"promoEligible"`
}

// Convert BillingRecord to Invoice
func convertToInvoices(records []*v1alpha1.BillingRecord) []billing.Invoice {
	invoices := make([]billing.Invoice, len(records))
	for i, record := range records {
		invoices[i] = billing.Invoice{
			ID:     record.CustomerName,
			Amount: record.Amount,
			Date:   parseDate(record.Date),
		}
	}
	return invoices
}

// parseDate converts a string date to time.Time
func parseDate(dateStr string) time.Time {
	t, err := time.Parse(time.RFC3339, dateStr)
	if err != nil {
		// Return a default date if parsing fails
		return time.Now()
	}
	return t
}

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          *v1alpha1.ResourceUsage `json:"usage"`
		Pricing        PricingModel            `json:"pricing"`
		BillingHistory []billing.Invoice       `json:"billingHistory"`
		PromoEligible  []string                `json:"promoEligible"`
	}{
		Usage:          usage,
		Pricing:        p.getPricingConfig(),
		BillingHistory: convertToInvoices(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
	}

	// Get current customer first
	customer, err := p.operator.GetCurrentCustomer(r.Context())
	if err != nil {
		http.Error(w, "Failed to get customer info", http.StatusInternalServerError)
		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(), customer.Name, req.Plan); err != nil {
		http.Error(w, "Failed to update plan", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

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

	cpuUsage, err := parseCPU(usage.CPU)
	if err != nil {
		log.Printf("failed to parse CPU usage: %v", err)
		return false
	}

	cpuLimit, err := parseCPU(limits.CPU)
	if err != nil {
		log.Printf("failed to parse CPU limit: %v", err)
		return false
	}
	if cpuUsage > cpuLimit {
		return false
	}

	memUsage, err := parseMemory(usage.Memory)
	if err != nil {
		log.Printf("failed to parse memory usage: %v", err)
		return false
	}

	memLimit, err := parseMemory(limits.Memory)
	if err != nil {
		log.Printf("failed to parse memory limit: %v", err)
		return false
	}
	if memUsage > memLimit {
		return false
	}

	if usage.Clusters > limits.Clusters {
		return false
	}

	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() PricingModel {
	// Implementation depends on your configuration system
	return PricingModel{}
}

func (p *Portal) handleResourceUsage(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 and limits
	usage, err := p.operator.GetResourceUsage(r.Context(), customer.Name)
	if err != nil {
		http.Error(w, "Failed to get resource usage", http.StatusInternalServerError)
		return
	}

	limits := customer.Spec.ResourceQuota

	// Parse CPU values
	cpuUsage, err := parseCPU(usage.CPU)
	if err != nil {
		log.Printf("failed to parse CPU usage: %v", err)
		cpuUsage = 0
	}

	cpuLimit, err := parseCPU(limits.CPU)
	if err != nil {
		log.Printf("failed to parse CPU limit: %v", err)
		cpuLimit = 0
	}

	// Parse memory values
	memUsage, err := parseMemory(usage.Memory)
	if err != nil {
		log.Printf("failed to parse memory usage: %v", err)
		memUsage = 0
	}

	memLimit, err := parseMemory(limits.Memory)
	if err != nil {
		log.Printf("failed to parse memory limit: %v", err)
		memLimit = 0
	}

	// Calculate percentages
	resourceUsage := ResourceUsage{
		CPU:                  usage.CPU,
		CPUPercentage:        float64(cpuUsage) / float64(cpuLimit) * 100,
		Memory:               usage.Memory,
		MemoryPercentage:     float64(memUsage) / float64(memLimit) * 100,
		Deployments:          usage.Deployments,
		DeploymentPercentage: float64(usage.Deployments) / float64(limits.MaxDeployments) * 100,
		Clusters:             usage.Clusters,
		ClusterPercentage:    float64(usage.Clusters) / float64(limits.MaxClusters) * 100,
	}

	// Return JSON response
	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(resourceUsage); err != nil {
		http.Error(w, "Failed to encode response", http.StatusInternalServerError)
		return
	}
}
