package command

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"github.com/tingly-dev/tingly-box/ai/quota/fetcher"

	"github.com/tingly-dev/tingly-box/ai/quota"
	"github.com/tingly-dev/tingly-box/internal/typ"
)

// ============== Kong Command Structures ==============

// QuotaCmdKong is the Kong version of quota command with streamlined behavior.
// The default behavior (no subcommand) is to list all quotas.
type QuotaCmdKong struct {
	// List quotas (default behavior) - this is the default command
	List    QuotaListCmdKong    `kong:"cmd,name='list',default='1',hidden,help='List all provider quotas (default)'"`
	Get     QuotaGetCmdKong     `kong:"cmd,help='Get provider quota details'"`
	Refresh QuotaRefreshCmdKong `kong:"cmd,help='Refresh quota data'"`
	Summary QuotaSummaryCmdKong `kong:"cmd,help='Show quota summary'"`
}

// QuotaListCmdKong lists all provider quotas with optional refresh
type QuotaListCmdKong struct {
	Refresh bool `kong:"flag,name='refresh',short='r',help='Refresh before listing'"`
}

func (q *QuotaListCmdKong) Run(appManager *AppManager) error {
	if q.Refresh {
		return runQuotaRefresh(appManager)
	}
	return runQuotaList(appManager)
}

// QuotaRefreshCmdKong refreshes quota data
// Supports optional provider argument to refresh specific provider
type QuotaRefreshCmdKong struct {
	Provider string `kong:"arg,optional,help='Provider name or UUID to refresh (refreshes all if omitted)'"`
}

func (q *QuotaRefreshCmdKong) Run(appManager *AppManager) error {
	if q.Provider == "" {
		return runQuotaRefresh(appManager)
	}
	return runQuotaRefreshProvider(appManager, q.Provider)
}

// QuotaSummaryCmdKong shows quota summary
type QuotaSummaryCmdKong struct{}

func (q *QuotaSummaryCmdKong) Run(appManager *AppManager) error {
	return runQuotaSummary(appManager)
}

// QuotaGetCmdKong shows details for a specific provider
// This was merged into the list command, but we keep a separate command for explicit "get" usage
type QuotaGetCmdKong struct {
	Provider string `kong:"arg,optional,help='Provider name or UUID'"`
	Refresh  bool   `kong:"flag,name='refresh',short='r',help='Refresh before displaying'"`
}

func (q *QuotaGetCmdKong) Run(appManager *AppManager) error {
	if q.Provider == "" {
		return runQuotaGetInteractive(appManager)
	}
	return runQuotaGet(appManager, q.Provider, q.Refresh)
}

// ============== Business Logic Functions ==============

// findProvider finds a provider by name or UUID (exact match).
func findProvider(appManager *AppManager, nameOrUUID string) (*typ.Provider, error) {
	providers := appManager.ListProviders()
	for _, p := range providers {
		if p.Name == nameOrUUID || p.UUID == nameOrUUID {
			return p, nil
		}
	}
	return nil, fmt.Errorf("provider not found: %s", nameOrUUID)
}

// createQuotaManager creates a quota manager for CLI use
func createQuotaManager(appManager *AppManager) (*quota.Manager, error) {
	// Create quota store
	config := appManager.AppConfig()
	store, err := quota.NewGormStore(config.ConfigDir(), logrus.StandardLogger())
	if err != nil {
		return nil, fmt.Errorf("failed to create quota store: %w", err)
	}

	// Create quota manager with default config
	qConfig := quota.DefaultConfig()
	qm := quota.NewManager(qConfig, store, appManager, logrus.StandardLogger())

	// Register all built-in fetchers
	fetcher.RegisterAll(qm, logrus.StandardLogger())

	return qm, nil
}

// runQuotaList lists all provider quotas
func runQuotaList(appManager *AppManager) error {
	ctx := context.Background()

	qm, err := createQuotaManager(appManager)
	if err != nil {
		return err
	}

	usages, err := qm.ListQuota(ctx)
	if err != nil {
		return fmt.Errorf("failed to get quota data: %w", err)
	}

	providers := appManager.ListProviders()
	if len(providers) == 0 {
		fmt.Println("No providers configured.")
		return nil
	}

	return displayQuotaForProviders(providers, usages, qm, ctx, true)
}

// runQuotaGet displays detailed quota for a specific provider
func runQuotaGet(appManager *AppManager, providerName string, refresh bool) error {
	ctx := context.Background()

	// Find provider by name or UUID
	provider, err := findProvider(appManager, providerName)
	if err != nil {
		return err
	}

	qm, err := createQuotaManager(appManager)
	if err != nil {
		return err
	}

	// Refresh if requested
	if refresh {
		fmt.Printf("🔄 Refreshing quota data for %s...\n", providerName)
		_, err := qm.RefreshProvider(ctx, provider.UUID)
		if err != nil {
			fmt.Printf("⚠️  Refresh failed: %v\n", err)
		} else {
			fmt.Println("✅ Refresh complete")
		}
	}

	// Get quota data
	usage, err := qm.GetQuota(ctx, provider.UUID)
	if err != nil {
		return fmt.Errorf("failed to get quota: %w", err)
	}

	printQuotaDetails(usage)
	return nil
}

// displayQuotaForProviders displays quota info for given providers
func displayQuotaForProviders(providers []*typ.Provider, usages []*quota.ProviderUsage, qm *quota.Manager, ctx context.Context, showSeparator bool) error {
	// Build a lookup from store data
	usageMap := make(map[string]*quota.ProviderUsage, len(usages))
	for _, u := range usages {
		usageMap[u.ProviderUUID] = u
	}

	for i, p := range providers {
		if u, ok := usageMap[p.UUID]; ok {
			printQuotaDetails(u)
		} else {
			// No data yet — show provider with "not fetched" status
			printQuotaDetails(&quota.ProviderUsage{
				ProviderUUID: p.UUID,
				ProviderName: p.Name,
				ProviderType: quota.ProviderType(p.APIStyle),
				LastError:    "no data — run 'quota refresh' to fetch",
			})
		}

		// Empty line between providers (except after last one)
		if showSeparator && i < len(providers)-1 {
			fmt.Println()
		}
	}

	return nil
}

// runQuotaGetInteractive runs interactive mode for get
func runQuotaGetInteractive(appManager *AppManager) error {
	providers := appManager.ListProviders()

	if len(providers) == 0 {
		fmt.Println("❌ No providers configured.")
		return nil
	}

	fmt.Println("\n📊 View Provider Quota")
	fmt.Println("\nSelect a provider:")

	for i, provider := range providers {
		status := "✅"
		if !provider.Enabled {
			status = "❌"
		}
		fmt.Printf("%d. %s %s (%s)\n", i+1, status, provider.Name, provider.UUID[:8])
	}

	fmt.Print("\nEnter provider number, name, or UUID: ")
	var input string
	fmt.Scanln(&input)
	input = strings.TrimSpace(input)

	var name string
	var num int
	if _, err := fmt.Sscanf(input, "%d", &num); err == nil && num > 0 && num <= len(providers) {
		name = providers[num-1].Name
	} else {
		// Try as name or UUID directly
		name = input
	}

	return runQuotaGet(appManager, name, false)
}

// runQuotaRefresh refreshes all provider quotas
func runQuotaRefresh(appManager *AppManager) error {
	ctx := context.Background()

	qm, err := createQuotaManager(appManager)
	if err != nil {
		return err
	}

	fmt.Println("🔄 Refreshing quota data for all providers...")

	usages, err := qm.Refresh(ctx)
	if err != nil {
		return fmt.Errorf("failed to refresh quota: %w", err)
	}

	fmt.Printf("✅ Refreshed %d provider(s)\n\n", len(usages))

	providers := appManager.ListProviders()
	return displayQuotaForProviders(providers, usages, qm, ctx, true)
}

// runQuotaRefreshProvider refreshes a specific provider
func runQuotaRefreshProvider(appManager *AppManager, providerName string) error {
	ctx := context.Background()

	// Find provider by name or UUID
	provider, err := findProvider(appManager, providerName)
	if err != nil {
		return err
	}

	qm, err := createQuotaManager(appManager)
	if err != nil {
		return err
	}

	fmt.Printf("🔄 Refreshing quota data for %s...\n", providerName)

	usage, err := qm.RefreshProvider(ctx, provider.UUID)
	if err != nil {
		return fmt.Errorf("failed to refresh quota: %w", err)
	}

	fmt.Println("✅ Refresh complete")
	printQuotaDetails(usage)

	return nil
}

// runQuotaSummary displays quota summary
func runQuotaSummary(appManager *AppManager) error {
	ctx := context.Background()

	qm, err := createQuotaManager(appManager)
	if err != nil {
		return err
	}

	summary, err := qm.Summary(ctx)
	if err != nil {
		return fmt.Errorf("failed to get summary: %w", err)
	}

	fmt.Println("\n📊 Quota Summary")
	fmt.Println(strings.Repeat("=", 50))
	fmt.Printf("Total Providers:   %d\n", summary.TotalProviders)
	fmt.Printf("✅ OK:              %d\n", summary.OKProviders)
	fmt.Printf("❌ Error:           %d\n", summary.ErrorProviders)
	fmt.Printf("⚠️  Warning (>80%%):  %d\n", summary.WarningProviders)
	fmt.Println(strings.Repeat("=", 50))

	if len(summary.ByType) > 0 {
		fmt.Println("\nBy Provider Type:")
		for pt, count := range summary.ByType {
			fmt.Printf("  %s: %d\n", pt, count)
		}
	}

	return nil
}

// printQuotaOverview prints a one-line overview of provider quota
func printQuotaOverview(usage *quota.ProviderUsage) {
	// Get primary window info
	primaryInfo := ""
	if usage.Primary != nil {
		primaryInfo = fmt.Sprintf(" | %s: %.1f%%", usage.Primary.Label, usage.Primary.UsedPercent)
	}

	// Add secondary indicator if exists
	secondaryIndicator := ""
	if usage.Secondary != nil {
		secondaryIndicator = " +"
	}

	if usage.LastError != "" {
		fmt.Printf("  %-20s%s%s\n", usage.ProviderName, primaryInfo, secondaryIndicator)
		fmt.Printf("  %-20s%s\n", "", "  error: "+usage.LastError)
	} else {
		fmt.Printf("  %-20s%s%s\n", usage.ProviderName, primaryInfo, secondaryIndicator)
	}
}

// printQuotaDetails prints detailed quota information
func printQuotaDetails(usage *quota.ProviderUsage) {
	fmt.Println("\n" + strings.Repeat("=", 70))
	fmt.Printf("📊 %s Quota Details\n", strings.ToUpper(usage.ProviderName))
	fmt.Println(strings.Repeat("=", 70))

	// Status
	status := "✅ OK"
	if usage.LastError != "" {
		status = fmt.Sprintf("❌ Error: %s", usage.LastError)
	}
	fmt.Printf("Status:     %s\n", status)
	fmt.Printf("Provider:   %s (%s)\n", usage.ProviderName, usage.ProviderType)
	fmt.Printf("Fetched:    %s\n", usage.FetchedAt.Format("2006-01-02 15:04:05"))

	// Account info
	if usage.Account != nil {
		fmt.Println("\n👤 Account:")
		if usage.Account.Name != "" {
			fmt.Printf("  Name: %s\n", usage.Account.Name)
		}
		if usage.Account.Email != "" {
			fmt.Printf("  Email: %s\n", usage.Account.Email)
		}
		if usage.Account.Tier != "" {
			fmt.Printf("  Tier: %s\n", usage.Account.Tier)
		}
	}

	// Display all quota groups (breakdowns + aggregate)
	fmt.Println("\n📊 Quota:")

	// First, display breakdowns if available
	if len(usage.Breakdowns) > 0 {
		// Group breakdowns by their Group field
		grouped := make(map[string][]*quota.UsageBreakdown)
		for _, bd := range usage.Breakdowns {
			grouped[bd.Group] = append(grouped[bd.Group], bd)
		}

		// Display each group with appropriate header
		for group, bds := range grouped {
			var groupHeader string
			switch group {
			case "model":
				groupHeader = "By Model"
			case "type":
				groupHeader = "By Type"
			case "region":
				groupHeader = "By Region"
			case "tier":
				groupHeader = "By Tier"
			default:
				groupHeader = "By " + strings.ToUpper(group[:1]) + group[1:]
			}

			fmt.Printf("\n  📦 %s:\n", groupHeader)
			for _, bd := range bds {
				fmt.Printf("    • %s:\n", bd.Label)
				for _, w := range bd.Windows {
					printUsageWindowInline(w, 4)
				}
			}
		}
		fmt.Println() // separator before aggregate
	}

	// Then display aggregate (primary/secondary/tertiary as "Total" group)
	aggregateLabel := "Total"
	if usage.Primary != nil || usage.Secondary != nil || usage.Tertiary != nil {
		fmt.Printf("  📊 %s:\n", aggregateLabel)
	}
	if usage.Primary != nil {
		printUsageWindowInline(usage.Primary, 3)
	}
	if usage.Secondary != nil {
		printUsageWindowInline(usage.Secondary, 3)
	}
	if usage.Tertiary != nil {
		printUsageWindowInline(usage.Tertiary, 3)
	}

	// Cost
	if usage.Cost != nil {
		fmt.Println("\n💰 Cost:")
		fmt.Printf("  Used:    $%.2f %s\n", usage.Cost.Used, usage.Cost.CurrencyCode)
		if usage.Cost.Limit > 0 {
			fmt.Printf("  Limit:   $%.2f %s\n", usage.Cost.Limit, usage.Cost.CurrencyCode)
			percent := (usage.Cost.Used / usage.Cost.Limit) * 100
			fmt.Printf("  Percent: %.1f%%\n", percent)
		}
		if usage.Cost.ResetsAt != nil {
			fmt.Printf("  Resets:  %s\n", usage.Cost.ResetsAt.Format("2006-01-02"))
		}
	}

	fmt.Println(strings.Repeat("=", 70))
}

// printUsageWindow prints a usage window
func printUsageWindow(w *quota.UsageWindow, indent int) {
	prefix := strings.Repeat("  ", indent)
	fmt.Printf("%sLabel:     %s\n", prefix, w.Label)
	fmt.Printf("%sType:      %s\n", prefix, w.Type)

	// For percentage-only quotas, show only percentage
	if w.Used == 0 && w.Limit == 0 && w.Unit == quota.UsageUnitPercent {
		fmt.Printf("%sUtilization: %.1f%%\n", prefix, w.UsedPercent)
	} else {
		fmt.Printf("%sUsed:      %s", prefix, formatUsageValue(w.Used, w.Unit))
		fmt.Printf(" / %s\n", formatUsageValue(w.Limit, w.Unit))
		fmt.Printf("%sPercent:   %.1f%%\n", prefix, w.UsedPercent)
	}

	if w.ResetsAt != nil {
		if time.Until(*w.ResetsAt) > 0 {
			fmt.Printf("%sResets in: %s\n", prefix, formatDuration(time.Until(*w.ResetsAt)))
		} else {
			fmt.Printf("%sResets at: %s\n", prefix, w.ResetsAt.Format("2006-01-02 15:04"))
		}
	}
}

// printUsageWindowInline prints a usage window on a single line (for breakdowns)
func printUsageWindowInline(w *quota.UsageWindow, indent int) {
	prefix := strings.Repeat("  ", indent)

	// For percentage-only quotas (Used=0, Limit=0), show only percentage
	if w.Used == 0 && w.Limit == 0 && w.Unit == quota.UsageUnitPercent {
		fmt.Printf("%s%s: %.1f%%", prefix, w.Label, w.UsedPercent)
	} else {
		fmt.Printf("%s%s: %s / %s (%.1f%%)", prefix, w.Label, formatUsageValue(w.Used, w.Unit), formatUsageValue(w.Limit, w.Unit), w.UsedPercent)
	}

	if w.ResetsAt != nil {
		if time.Until(*w.ResetsAt) > 0 {
			fmt.Printf(" — resets in %s", formatDuration(time.Until(*w.ResetsAt)))
		}
	}
	fmt.Println()
}

// formatUsageValue formats a usage value with appropriate unit
func formatUsageValue(value float64, unit quota.UsageUnit) string {
	switch unit {
	case quota.UsageUnitTokens:
		if value >= 1000000 {
			return fmt.Sprintf("%.1fM", value/1000000)
		} else if value >= 1000 {
			return fmt.Sprintf("%.1fK", value/1000)
		}
		return fmt.Sprintf("%.0f", value)
	case quota.UsageUnitRequests:
		return fmt.Sprintf("%.0f", value)
	case quota.UsageUnitCurrency:
		return fmt.Sprintf("$%.2f", value)
	case quota.UsageUnitPercent:
		return fmt.Sprintf("%.1f%%", value)
	case quota.UsageUnitCredits:
		if value >= 1000000 {
			return fmt.Sprintf("%.1fM", value/1000000)
		} else if value >= 1000 {
			return fmt.Sprintf("%.1fK", value/1000)
		}
		return fmt.Sprintf("%.0f", value)
	default:
		return fmt.Sprintf("%.2f", value)
	}
}

// formatDuration formats a duration in human-readable form
func formatDuration(d time.Duration) string {
	if d < time.Minute {
		return fmt.Sprintf("%d seconds", int(d.Seconds()))
	} else if d < time.Hour {
		return fmt.Sprintf("%d minutes", int(d.Minutes()))
	} else if d < 24*time.Hour {
		hours := int(d.Hours())
		mins := int(d.Minutes()) % 60
		return fmt.Sprintf("%dh %dm", hours, mins)
	} else {
		days := int(d.Hours() / 24)
		hours := int(d.Hours()) % 24
		return fmt.Sprintf("%dd %dh", days, hours)
	}
}
