package report

import (
	"log"
	"sort"
	"time"

	"gitea.dooplex.hu/admin/felhom-controller/internal/metrics"
	"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
)

// controllerContainerName is the Docker container name for the controller itself.
const controllerContainerName = "felhom-controller"

// buildAppTelemetrySection collects metrics telemetry and log scans for all
// non-protected, deployed stacks plus the controller itself, and returns
// per-app telemetry data.
func buildAppTelemetrySection(stackMgr *stacks.Manager, metricsStore *metrics.MetricsStore, logger *log.Logger) []AppTelemetry {
	allStacks := stackMgr.GetStacks()

	// 1. Get metrics telemetry (last 15 minutes)
	var telemetry []metrics.ContainerTelemetry
	if metricsStore != nil {
		var err error
		telemetry, err = metricsStore.GetContainerTelemetry(time.Now().Add(-15 * time.Minute))
		if err != nil && logger != nil {
			logger.Printf("[WARN] Telemetry metrics query failed: %v", err)
		}
	}

	// 2. Collect non-protected container names for log scan
	var containerNames []string
	for _, s := range allStacks {
		if s.Protected || !s.Deployed || !isStackRunning(s.State) {
			continue
		}
		for _, c := range s.Containers {
			containerNames = append(containerNames, c.Name)
		}
	}

	// 3. Include controller container in log scan
	containerNames = append(containerNames, controllerContainerName)

	// 4. Scan logs (includes controller)
	logs := metrics.ScanContainerLogs(containerNames, 15*time.Minute, logger)

	// 5. Build per-app telemetry (stacks only)
	result := buildAppTelemetry(allStacks, telemetry, logs)

	// 6. Append controller telemetry entry
	if ctrl := buildControllerTelemetry(telemetry, logs); ctrl != nil {
		result = append(result, *ctrl)
		sort.Slice(result, func(i, j int) bool {
			return result[i].AppName < result[j].AppName
		})
	}

	if result == nil {
		result = []AppTelemetry{}
	}
	return result
}

// buildAppTelemetry aggregates container-level telemetry and log data into per-stack AppTelemetry entries.
func buildAppTelemetry(allStacks []stacks.Stack, telemetry []metrics.ContainerTelemetry, logs []metrics.ContainerLogSummary) []AppTelemetry {
	// Build lookup maps
	telemetryMap := make(map[string]metrics.ContainerTelemetry, len(telemetry))
	for _, ct := range telemetry {
		telemetryMap[ct.ContainerName] = ct
	}

	logMap := make(map[string]metrics.ContainerLogSummary, len(logs))
	for _, ls := range logs {
		logMap[ls.ContainerName] = ls
	}

	var result []AppTelemetry

	for _, s := range allStacks {
		if s.Protected || !s.Deployed || !isStackRunning(s.State) {
			continue
		}

		app := AppTelemetry{
			AppName:         s.Name,
			DisplayName:     s.Meta.DisplayName,
			CatalogEstimate: s.Meta.Resources.MemRequest,
			CatalogLimit:    s.Meta.Resources.MemLimit,
		}

		var mergedIssues []metrics.LogIssue

		for _, c := range s.Containers {
			app.Containers = append(app.Containers, c.Name)

			if ct, ok := telemetryMap[c.Name]; ok {
				app.MemoryCurrentMB += ct.MemoryCurrentMB
				app.MemoryAvgMB += ct.MemoryAvgMB
				app.MemoryPeakMB += ct.MemoryPeakMB
				app.CPUAvgPercent += ct.CPUAvgPercent
			}

			if ls, ok := logMap[c.Name]; ok {
				app.LogErrors += ls.ErrorCount
				app.LogWarnings += ls.WarnCount
				mergedIssues = append(mergedIssues, ls.RecentIssues...)
			}
		}

		if app.Containers == nil {
			app.Containers = []string{}
		}

		// Sort and cap merged issues at 10
		sort.Slice(mergedIssues, func(i, j int) bool {
			if mergedIssues[i].Count != mergedIssues[j].Count {
				return mergedIssues[i].Count > mergedIssues[j].Count
			}
			return mergedIssues[i].LastSeen.After(mergedIssues[j].LastSeen)
		})
		if len(mergedIssues) > 10 {
			mergedIssues = mergedIssues[:10]
		}
		app.Issues = mergedIssues

		result = append(result, app)
	}

	// Sort by app name
	sort.Slice(result, func(i, j int) bool {
		return result[i].AppName < result[j].AppName
	})

	if result == nil {
		result = []AppTelemetry{}
	}
	return result
}

// buildControllerTelemetry creates a telemetry entry for the controller container.
// Returns nil if no metrics or log data is available.
func buildControllerTelemetry(telemetry []metrics.ContainerTelemetry, logs []metrics.ContainerLogSummary) *AppTelemetry {
	app := AppTelemetry{
		AppName:     controllerContainerName,
		DisplayName: "Felhom Controller",
		Containers:  []string{controllerContainerName},
	}

	// Find metrics for the controller container
	for _, ct := range telemetry {
		if ct.ContainerName == controllerContainerName {
			app.MemoryCurrentMB = ct.MemoryCurrentMB
			app.MemoryAvgMB = ct.MemoryAvgMB
			app.MemoryPeakMB = ct.MemoryPeakMB
			app.CPUAvgPercent = ct.CPUAvgPercent
			break
		}
	}

	// Find log scan results for the controller container
	for _, ls := range logs {
		if ls.ContainerName == controllerContainerName {
			app.LogErrors = ls.ErrorCount
			app.LogWarnings = ls.WarnCount
			issues := ls.RecentIssues
			if len(issues) > 10 {
				issues = issues[:10]
			}
			app.Issues = issues
			break
		}
	}

	// Only include if we have at least metrics or log data
	if app.MemoryCurrentMB == 0 && app.MemoryAvgMB == 0 && app.LogErrors == 0 && app.LogWarnings == 0 {
		return nil
	}

	return &app
}

// isStackRunning returns true if the stack has containers actively running
// (running, starting, or unhealthy but still up). Stopped, exited, deploying
// etc. are excluded to avoid sending zero-value telemetry to the hub.
func isStackRunning(state stacks.ContainerState) bool {
	switch state {
	case stacks.StateRunning, stacks.StateStarting, stacks.StateUnhealthy, stacks.StateRestarting:
		return true
	default:
		return false
	}
}

// BuildAppTelemetryForDebug runs the full telemetry collection pipeline
// (metrics query + log scan) and returns per-app telemetry data.
// Used by the debug endpoint to preview telemetry without pushing to hub.
func BuildAppTelemetryForDebug(stackMgr *stacks.Manager, metricsStore *metrics.MetricsStore, logger *log.Logger) []AppTelemetry {
	return buildAppTelemetrySection(stackMgr, metricsStore, logger)
}
