package handlers

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/Fimeg/RedFlag/aggregator-agent/internal/acknowledgment"
	"github.com/Fimeg/RedFlag/aggregator-agent/internal/client"
	"github.com/Fimeg/RedFlag/aggregator-agent/internal/config"
	"github.com/Fimeg/RedFlag/aggregator-agent/internal/models"
	"github.com/Fimeg/RedFlag/aggregator-agent/internal/orchestrator"
)

// reportLogWithAck reports a command log to the server and tracks it for acknowledgment
func reportLogWithAck(apiClient *client.Client, cfg *config.Config, ackTracker *acknowledgment.Tracker, logReport client.LogReport) error {
	// Track this command result as pending acknowledgment
	ackTracker.Add(logReport.CommandID)

	// Save acknowledgment state immediately
	if err := ackTracker.Save(); err != nil {
		log.Printf("Warning: Failed to save acknowledgment for command %s: %v", logReport.CommandID, err)
	}

	// Report the log to the server
	if err := apiClient.ReportLog(cfg.AgentID, logReport); err != nil {
		// If reporting failed, increment retry count but don't remove from pending
		ackTracker.IncrementRetry(logReport.CommandID)
		return err
	}

	return nil
}

// HandleScanStorage scans disk usage metrics only
func HandleScanStorage(apiClient *client.Client, cfg *config.Config, ackTracker *acknowledgment.Tracker, orch *orchestrator.Orchestrator, commandID string) error {
	log.Println("Scanning storage...")

	ctx := context.Background()
	startTime := time.Now()

	// Execute storage scanner
	result, err := orch.ScanSingle(ctx, "storage")
	if err != nil {
		return fmt.Errorf("failed to scan storage: %w", err)
	}

	// Format results
	results := []orchestrator.ScanResult{result}
	stdout, stderr, exitCode := orchestrator.FormatScanSummary(results)

	duration := time.Since(startTime)
	stdout += fmt.Sprintf("\nStorage scan completed in %.2f seconds\n", duration.Seconds())

	// Report storage metrics to server using dedicated endpoint
	// Use proper StorageMetricReport with clean field names
	storageScanner := orchestrator.NewStorageScanner(cfg.AgentVersion)
	var metrics []orchestrator.StorageMetric // Declare outside if block for ReportLog access
	if storageScanner.IsAvailable() {
		var err error
		metrics, err = storageScanner.ScanStorage()
		if err != nil {
			return fmt.Errorf("failed to scan storage metrics: %w", err)
		}

		if len(metrics) > 0 {
			// Convert from orchestrator.StorageMetric to models.StorageMetric
			metricItems := make([]models.StorageMetric, 0, len(metrics))
			for _, m := range metrics {
				item := models.StorageMetric{
					Mountpoint:     m.Mountpoint,
					Device:         m.Device,
					DiskType:       m.DiskType,
					Filesystem:     m.Filesystem,
					TotalBytes:     m.TotalBytes,
					UsedBytes:      m.UsedBytes,
					AvailableBytes: m.AvailableBytes,
					UsedPercent:    m.UsedPercent,
					IsRoot:         m.IsRoot,
					IsLargest:      m.IsLargest,
					Severity:       m.Severity,
					Metadata:       m.Metadata,
				}
				metricItems = append(metricItems, item)
			}

			report := models.StorageMetricReport{
				AgentID:   cfg.AgentID,
				CommandID: commandID,
				Timestamp: time.Now(),
				Metrics:   metricItems,
			}

			if err := apiClient.ReportStorageMetrics(cfg.AgentID, report); err != nil {
				return fmt.Errorf("failed to report storage metrics: %w", err)
			}

			log.Printf("[INFO] [storage] Successfully reported %d storage metrics to server\n", len(metrics))
		}
	}

	// Create history entry for unified view with proper formatting
	logReport := client.LogReport{
		CommandID:       commandID,
		Action:          "scan_storage",
		Result:          map[bool]string{true: "success", false: "failure"}[exitCode == 0],
		Stdout:          stdout,
		Stderr:          stderr,
		ExitCode:        exitCode,
		DurationSeconds: int(duration.Seconds()),
		Metadata: map[string]string{
			"subsystem_label": "Disk Usage",
			"subsystem":       "storage",
			"metrics_count":   fmt.Sprintf("%d", len(metrics)),
		},
	}
	if err := reportLogWithAck(apiClient, cfg, ackTracker, logReport); err != nil {
		log.Printf("[ERROR] [agent] [storage] report_log_failed: %v", err)
		log.Printf("[HISTORY] [agent] [storage] report_log_failed error=\"%v\" timestamp=%s", err, time.Now().Format(time.RFC3339))
	} else {
		log.Printf("[INFO] [agent] [storage] history_log_created command_id=%s timestamp=%s", commandID, time.Now().Format(time.RFC3339))
		log.Printf("[HISTORY] [agent] [scan_storage] log_created agent_id=%s command_id=%s result=%s timestamp=%s", cfg.AgentID, commandID, map[bool]string{true: "success", false: "failure"}[exitCode == 0], time.Now().Format(time.RFC3339))
	}

	return nil
}

// HandleScanSystem scans system metrics (CPU, memory, processes, uptime)
func HandleScanSystem(apiClient *client.Client, cfg *config.Config, ackTracker *acknowledgment.Tracker, orch *orchestrator.Orchestrator, commandID string) error {
	log.Println("Scanning system metrics...")

	ctx := context.Background()
	startTime := time.Now()

	// Execute system scanner
	result, err := orch.ScanSingle(ctx, "system")
	if err != nil {
		return fmt.Errorf("failed to scan system: %w", err)
	}

	// Format results
	results := []orchestrator.ScanResult{result}
	stdout, stderr, exitCode := orchestrator.FormatScanSummary(results)

	duration := time.Since(startTime)
	stdout += fmt.Sprintf("\nSystem scan completed in %.2f seconds\n", duration.Seconds())

	// Report system metrics to server using dedicated endpoint
	// Get system scanner and use proper interface
	systemScanner := orchestrator.NewSystemScanner(cfg.AgentVersion)
	var metrics []orchestrator.SystemMetric // Declare outside if block for ReportLog access
	if systemScanner.IsAvailable() {
		var err error
		metrics, err = systemScanner.ScanSystem()
		if err != nil {
			return fmt.Errorf("failed to scan system metrics: %w", err)
		}

		if len(metrics) > 0 {
			// Convert SystemMetric to MetricsReportItem for API call
			metricItems := make([]client.MetricsReportItem, 0, len(metrics))
			for _, metric := range metrics {
				item := client.MetricsReportItem{
					PackageType:      "system",
					PackageName:      metric.MetricName,
					CurrentVersion:   metric.CurrentValue,
					AvailableVersion: metric.AvailableValue,
					Severity:         metric.Severity,
					RepositorySource: metric.MetricType,
					Metadata:         metric.Metadata,
				}
				metricItems = append(metricItems, item)
			}

			report := client.MetricsReport{
				CommandID: commandID,
				Timestamp: time.Now(),
				Metrics:   metricItems,
			}

			if err := apiClient.ReportMetrics(cfg.AgentID, report); err != nil {
				return fmt.Errorf("failed to report system metrics: %w", err)
			}

			log.Printf("[INFO] [agent] [system] Reported %d system metrics to server\n", len(metrics))
		}
	}

	// Create history entry for unified view with proper formatting
	logReport := client.LogReport{
		CommandID:       commandID,
		Action:          "scan_system",
		Result:          map[bool]string{true: "success", false: "failure"}[exitCode == 0],
		Stdout:          stdout,
		Stderr:          stderr,
		ExitCode:        exitCode,
		DurationSeconds: int(duration.Seconds()),
		Metadata: map[string]string{
			"subsystem_label": "System Metrics",
			"subsystem":       "system",
			"metrics_count":   fmt.Sprintf("%d", len(metrics)),
		},
	}
	if err := reportLogWithAck(apiClient, cfg, ackTracker, logReport); err != nil {
		log.Printf("[ERROR] [agent] [system] report_log_failed: %v", err)
		log.Printf("[HISTORY] [agent] [system] report_log_failed error=\"%v\" timestamp=%s", err, time.Now().Format(time.RFC3339))
	} else {
		log.Printf("[INFO] [agent] [system] history_log_created command_id=%s timestamp=%s", commandID, time.Now().Format(time.RFC3339))
		log.Printf("[HISTORY] [agent] [scan_system] log_created agent_id=%s command_id=%s result=%s timestamp=%s", cfg.AgentID, commandID, map[bool]string{true: "success", false: "failure"}[exitCode == 0], time.Now().Format(time.RFC3339))
	}

	return nil
}

// HandleScanDocker scans Docker image updates only
func HandleScanDocker(apiClient *client.Client, cfg *config.Config, ackTracker *acknowledgment.Tracker, orch *orchestrator.Orchestrator, commandID string) error {
	log.Println("Scanning Docker images...")

	ctx := context.Background()
	startTime := time.Now()

	// Execute Docker scanner
	result, err := orch.ScanSingle(ctx, "docker")
	if err != nil {
		return fmt.Errorf("failed to scan Docker: %w", err)
	}

	// Format results
	results := []orchestrator.ScanResult{result}
	stdout, stderr, exitCode := orchestrator.FormatScanSummary(results)

	duration := time.Since(startTime)
	stdout += fmt.Sprintf("\nDocker scan completed in %.2f seconds\n", duration.Seconds())

	// Report Docker images to server using dedicated endpoint
	// Get Docker scanner and use proper interface
	dockerScanner, err := orchestrator.NewDockerScanner()
	if err != nil {
		return fmt.Errorf("failed to create Docker scanner: %w", err)
	}
	defer dockerScanner.Close()

	var images []orchestrator.DockerImage // Declare outside if block for ReportLog access
	var updateCount int                   // Declare outside if block for ReportLog access
	if dockerScanner.IsAvailable() {
		images, err = dockerScanner.ScanDocker()
		if err != nil {
			return fmt.Errorf("failed to scan Docker images: %w", err)
		}

		// Always report all Docker images (not just those with updates)
		updateCount = 0 // Reset for counting
		if len(images) > 0 {
			// Convert DockerImage to DockerReportItem for API call
			imageItems := make([]client.DockerReportItem, 0, len(images))
			for _, image := range images {
				item := client.DockerReportItem{
					PackageType:      "docker_image",
					PackageName:      image.ImageName,
					CurrentVersion:   image.ImageID,
					AvailableVersion: image.LatestImageID,
					Severity:         image.Severity,
					RepositorySource: image.RepositorySource,
					Metadata:         image.Metadata,
				}
				imageItems = append(imageItems, item)
			}

			report := client.DockerReport{
				CommandID: commandID,
				Timestamp: time.Now(),
				Images:    imageItems,
			}

			if err := apiClient.ReportDockerImages(cfg.AgentID, report); err != nil {
				return fmt.Errorf("failed to report Docker images: %w", err)
			}

			for _, image := range images {
				if image.HasUpdate {
					updateCount++
				}
			}
			log.Printf("[INFO] [agent] [docker] Reported %d Docker images (%d with updates) to server\n", len(images), updateCount)
		} else {
			log.Println("[INFO] [agent] [docker] No Docker images found")
		}
	} else {
		log.Println("[INFO] [agent] [docker] Docker not available on this system")
	}

	// Create history entry for unified view with proper formatting
	logReport := client.LogReport{
		CommandID:       commandID,
		Action:          "scan_docker",
		Result:          map[bool]string{true: "success", false: "failure"}[exitCode == 0],
		Stdout:          stdout,
		Stderr:          stderr,
		ExitCode:        exitCode,
		DurationSeconds: int(duration.Seconds()),
		Metadata: map[string]string{
			"subsystem_label": "Docker Images",
			"subsystem":       "docker",
			"images_count":    fmt.Sprintf("%d", len(images)),
			"updates_found":   fmt.Sprintf("%d", updateCount),
		},
	}
	if err := reportLogWithAck(apiClient, cfg, ackTracker, logReport); err != nil {
		log.Printf("[ERROR] [agent] [docker] report_log_failed: %v", err)
		log.Printf("[HISTORY] [agent] [docker] report_log_failed error=\"%v\" timestamp=%s", err, time.Now().Format(time.RFC3339))
	} else {
		log.Printf("[INFO] [agent] [docker] history_log_created command_id=%s timestamp=%s", commandID, time.Now().Format(time.RFC3339))
		log.Printf("[HISTORY] [agent] [scan_docker] log_created agent_id=%s command_id=%s result=%s timestamp=%s", cfg.AgentID, commandID, map[bool]string{true: "success", false: "failure"}[exitCode == 0], time.Now().Format(time.RFC3339))
	}

	return nil
}

// HandleScanAPT scans APT package updates only
func HandleScanAPT(apiClient *client.Client, cfg *config.Config, ackTracker *acknowledgment.Tracker, orch *orchestrator.Orchestrator, commandID string) error {
	log.Println("Scanning APT packages...")

	ctx := context.Background()
	startTime := time.Now()

	// Execute APT scanner
	result, err := orch.ScanSingle(ctx, "apt")
	if err != nil {
		return fmt.Errorf("failed to scan APT: %w", err)
	}

	// Format results
	results := []orchestrator.ScanResult{result}
	stdout, stderr, exitCode := orchestrator.FormatScanSummary(results)

	duration := time.Since(startTime)
	stdout += fmt.Sprintf("\nAPT scan completed in %.2f seconds\n", duration.Seconds())

	// Report APT updates to server if any were found
	// Declare updates at function scope for ReportLog access
	var updates []client.UpdateReportItem
	if result.Status == "success" && len(result.Updates) > 0 {
		updates = result.Updates
		report := client.UpdateReport{
			CommandID: commandID,
			Timestamp: time.Now(),
			Updates:   updates,
		}

		if err := apiClient.ReportUpdates(cfg.AgentID, report); err != nil {
			return fmt.Errorf("failed to report APT updates: %w", err)
		}

		log.Printf("[INFO] [agent] [apt] Successfully reported %d APT updates to server\n", len(updates))
	}

	// Create history entry for unified view with proper formatting
	logReport := client.LogReport{
		CommandID:       commandID,
		Action:          "scan_apt",
		Result:          map[bool]string{true: "success", false: "failure"}[exitCode == 0],
		Stdout:          stdout,
		Stderr:          stderr,
		ExitCode:        exitCode,
		DurationSeconds: int(duration.Seconds()),
		Metadata: map[string]string{
			"subsystem_label": "APT Packages",
			"subsystem":       "apt",
			"updates_found":   fmt.Sprintf("%d", len(updates)),
		},
	}
	if err := reportLogWithAck(apiClient, cfg, ackTracker, logReport); err != nil {
		log.Printf("[ERROR] [agent] [apt] report_log_failed: %v", err)
		log.Printf("[HISTORY] [agent] [apt] report_log_failed error=\"%v\" timestamp=%s", err, time.Now().Format(time.RFC3339))
	} else {
		log.Printf("[INFO] [agent] [apt] history_log_created command_id=%s timestamp=%s", commandID, time.Now().Format(time.RFC3339))
		log.Printf("[HISTORY] [agent] [scan_apt] log_created agent_id=%s command_id=%s result=%s timestamp=%s", cfg.AgentID, commandID, map[bool]string{true: "success", false: "failure"}[exitCode == 0], time.Now().Format(time.RFC3339))
	}

	return nil
}

// HandleScanDNF scans DNF package updates only
func HandleScanDNF(apiClient *client.Client, cfg *config.Config, ackTracker *acknowledgment.Tracker, orch *orchestrator.Orchestrator, commandID string) error {
	log.Println("Scanning DNF packages...")

	ctx := context.Background()
	startTime := time.Now()

	// Execute DNF scanner
	result, err := orch.ScanSingle(ctx, "dnf")
	if err != nil {
		return fmt.Errorf("failed to scan DNF: %w", err)
	}

	// Format results
	results := []orchestrator.ScanResult{result}
	stdout, stderr, exitCode := orchestrator.FormatScanSummary(results)

	duration := time.Since(startTime)
	stdout += fmt.Sprintf("\nDNF scan completed in %.2f seconds\n", duration.Seconds())

	// Report DNF updates to server if any were found
	// Declare updates at function scope for ReportLog access
	var updates []client.UpdateReportItem
	if result.Status == "success" && len(result.Updates) > 0 {
		updates = result.Updates
		report := client.UpdateReport{
			CommandID: commandID,
			Timestamp: time.Now(),
			Updates:   updates,
		}

		if err := apiClient.ReportUpdates(cfg.AgentID, report); err != nil {
			return fmt.Errorf("failed to report DNF updates: %w", err)
		}

		log.Printf("[INFO] [agent] [dnf] Successfully reported %d DNF updates to server\n", len(updates))
	}

	// Create history entry for unified view with proper formatting
	logReport := client.LogReport{
		CommandID:       commandID,
		Action:          "scan_dnf",
		Result:          map[bool]string{true: "success", false: "failure"}[exitCode == 0],
		Stdout:          stdout,
		Stderr:          stderr,
		ExitCode:        exitCode,
		DurationSeconds: int(duration.Seconds()),
		Metadata: map[string]string{
			"subsystem_label": "DNF Packages",
			"subsystem":       "dnf",
			"updates_found":   fmt.Sprintf("%d", len(updates)),
		},
	}
	if err := reportLogWithAck(apiClient, cfg, ackTracker, logReport); err != nil {
		log.Printf("[ERROR] [agent] [dnf] report_log_failed: %v", err)
		log.Printf("[HISTORY] [agent] [dnf] report_log_failed error=\"%v\" timestamp=%s", err, time.Now().Format(time.RFC3339))
	} else {
		log.Printf("[INFO] [agent] [dnf] history_log_created command_id=%s timestamp=%s", commandID, time.Now().Format(time.RFC3339))
		log.Printf("[HISTORY] [agent] [scan_dnf] log_created agent_id=%s command_id=%s result=%s timestamp=%s", cfg.AgentID, commandID, map[bool]string{true: "success", false: "failure"}[exitCode == 0], time.Now().Format(time.RFC3339))
	}

	return nil
}

// HandleScanWindows scans Windows Updates only
func HandleScanWindows(apiClient *client.Client, cfg *config.Config, ackTracker *acknowledgment.Tracker, orch *orchestrator.Orchestrator, commandID string) error {
	log.Println("Scanning Windows Updates...")

	ctx := context.Background()
	startTime := time.Now()

	// Execute Windows Update scanner
	result, err := orch.ScanSingle(ctx, "windows")
	if err != nil {
		return fmt.Errorf("failed to scan Windows Updates: %w", err)
	}

	// Format results
	results := []orchestrator.ScanResult{result}
	stdout, stderr, exitCode := orchestrator.FormatScanSummary(results)

	duration := time.Since(startTime)
	stdout += fmt.Sprintf("\nWindows Update scan completed in %.2f seconds\n", duration.Seconds())

	// Report Windows updates to server if any were found
	// Declare updates at function scope for ReportLog access
	var updates []client.UpdateReportItem
	if result.Status == "success" && len(result.Updates) > 0 {
		updates = result.Updates
		report := client.UpdateReport{
			CommandID: commandID,
			Timestamp: time.Now(),
			Updates:   updates,
		}

		if err := apiClient.ReportUpdates(cfg.AgentID, report); err != nil {
			return fmt.Errorf("failed to report Windows updates: %w", err)
		}

		log.Printf("[INFO] [agent] [windows] Successfully reported %d Windows updates to server\n", len(updates))
	}

	// Create history entry for unified view with proper formatting
	logReport := client.LogReport{
		CommandID:       commandID,
		Action:          "scan_windows",
		Result:          map[bool]string{true: "success", false: "failure"}[exitCode == 0],
		Stdout:          stdout,
		Stderr:          stderr,
		ExitCode:        exitCode,
		DurationSeconds: int(duration.Seconds()),
		Metadata: map[string]string{
			"subsystem_label": "Windows Updates",
			"subsystem":       "windows",
			"updates_found":   fmt.Sprintf("%d", len(updates)),
		},
	}
	if err := reportLogWithAck(apiClient, cfg, ackTracker, logReport); err != nil {
		log.Printf("[ERROR] [agent] [windows] report_log_failed: %v", err)
		log.Printf("[HISTORY] [agent] [windows] report_log_failed error=\"%v\" timestamp=%s", err, time.Now().Format(time.RFC3339))
	} else {
		log.Printf("[INFO] [agent] [windows] history_log_created command_id=%s timestamp=%s", commandID, time.Now().Format(time.RFC3339))
		log.Printf("[HISTORY] [agent] [scan_windows] log_created agent_id=%s command_id=%s result=%s timestamp=%s", cfg.AgentID, commandID, map[bool]string{true: "success", false: "failure"}[exitCode == 0], time.Now().Format(time.RFC3339))
	}

	return nil
}

// HandleScanWinget scans Winget package updates only
func HandleScanWinget(apiClient *client.Client, cfg *config.Config, ackTracker *acknowledgment.Tracker, orch *orchestrator.Orchestrator, commandID string) error {
	log.Println("Scanning Winget packages...")

	ctx := context.Background()
	startTime := time.Now()

	// Execute Winget scanner
	result, err := orch.ScanSingle(ctx, "winget")
	if err != nil {
		return fmt.Errorf("failed to scan Winget: %w", err)
	}

	// Format results
	results := []orchestrator.ScanResult{result}
	stdout, stderr, exitCode := orchestrator.FormatScanSummary(results)

	duration := time.Since(startTime)
	stdout += fmt.Sprintf("\nWinget scan completed in %.2f seconds\n", duration.Seconds())

	// Report Winget updates to server if any were found
	// Declare updates at function scope for ReportLog access
	var updates []client.UpdateReportItem
	if result.Status == "success" && len(result.Updates) > 0 {
		updates = result.Updates
		report := client.UpdateReport{
			CommandID: commandID,
			Timestamp: time.Now(),
			Updates:   updates,
		}

		if err := apiClient.ReportUpdates(cfg.AgentID, report); err != nil {
			return fmt.Errorf("failed to report Winget updates: %w", err)
		}

		log.Printf("[INFO] [agent] [winget] Successfully reported %d Winget updates to server\n", len(updates))
	}

	// Create history entry for unified view with proper formatting
	logReport := client.LogReport{
		CommandID:       commandID,
		Action:          "scan_winget",
		Result:          map[bool]string{true: "success", false: "failure"}[exitCode == 0],
		Stdout:          stdout,
		Stderr:          stderr,
		ExitCode:        exitCode,
		DurationSeconds: int(duration.Seconds()),
		Metadata: map[string]string{
			"subsystem_label": "Winget Packages",
			"subsystem":       "winget",
			"updates_found":   fmt.Sprintf("%d", len(updates)),
		},
	}
	if err := reportLogWithAck(apiClient, cfg, ackTracker, logReport); err != nil {
		log.Printf("[ERROR] [agent] [winget] report_log_failed: %v", err)
		log.Printf("[HISTORY] [agent] [winget] report_log_failed error=\"%v\" timestamp=%s", err, time.Now().Format(time.RFC3339))
	} else {
		log.Printf("[INFO] [agent] [winget] history_log_created command_id=%s timestamp=%s", commandID, time.Now().Format(time.RFC3339))
		log.Printf("[HISTORY] [agent] [scan_winget] log_created agent_id=%s command_id=%s result=%s timestamp=%s", cfg.AgentID, commandID, map[bool]string{true: "success", false: "failure"}[exitCode == 0], time.Now().Format(time.RFC3339))
	}

	return nil
}
