/*
Copyright © 2025 cavaliba.com
*/
package poller

import (
	"fmt"
	"sync"
	"time"

	"github.com/spf13/viper"
)

// workItem represents a monitor to check along with its position in the results
type workItem struct {
	index   int
	monitor Monitor
}

// RunParallel retrieves worklist and processes monitors concurrently with worker pool
func RunParallel() error {
	verbose := viper.GetBool("verbose")

	// Get parallel workers from viper, default to 4
	numWorkers := viper.GetInt("parallel")
	if numWorkers <= 0 {
		numWorkers = 4
	}

	if verbose {
		fmt.Println("Start cavpoller (parallel mode)")
		fmt.Println("=================================")
		fmt.Printf("Worker pool size: %d\n", numWorkers)
	}

	// 1. Retrieve worklist
	worklist, err := GetWorklist()
	if err != nil {
		return fmt.Errorf("failed to get worklist: %w", err)
	}

	if verbose {
		fmt.Printf("Received %d monitors\n", len(worklist.Monitors))
	}

	start := time.Now()

	// Pre-allocate results slice
	results := make([]CheckResult, len(worklist.Monitors))

	// Create work queue channel
	workQueue := make(chan workItem, numWorkers)

	// Track completion with WaitGroup
	var wg sync.WaitGroup

	// Launch worker pool
	for i := 0; i < numWorkers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			// Worker loop: receive from work queue, execute check, write to results
			for work := range workQueue {
				result := SingleCheck(work.monitor)
				results[work.index] = result
			}
		}()
	}

	// Send work to queue (in separate goroutine to prevent blocking)
	go func() {
		for i, monitor := range worklist.Monitors {
			workQueue <- workItem{index: i, monitor: monitor}
		}
		close(workQueue) // Signal workers that no more work coming
	}()

	// Wait for all workers to finish
	wg.Wait()

	// Print all results in order
	for i, result := range results {
		fmt.Printf("[%d/%d] %-30s %v %d %v\n",
			i+1,
			len(worklist.Monitors),
			result.Name,
			result.Status,
			result.HTTPCode,
			result.Duration)
	}

	// Create JobResponse and send to API
	jobResponse := JobResponse{
		Results: results,
	}

	err = SendResponse(jobResponse)
	if err != nil {
		return fmt.Errorf("failed to send response: %w", err)
	}

	fmt.Println("Response sent to API")

	// Calculate and print duration
	duration := time.Since(start)
	fmt.Printf("Done - duration = %v\n", duration)

	return nil
}

// RunSequence retrieves worklist and processes all monitors sequentially
func RunSequence() error {
	verbose := viper.GetBool("verbose")

	if verbose {
		fmt.Println("Start cavpoller (sequential mode)")
		fmt.Println("=================================")
	}

	// 1. Retrieve worklist
	worklist, err := GetWorklist()
	if err != nil {
		return fmt.Errorf("failed to get worklist: %w", err)
	}

	// if verbose {
	// 	fmt.Println(worklist)
	// }

	start := time.Now()

	// Create JobResponse to collect results
	jobResponse := JobResponse{
		Results: make([]CheckResult, 0, len(worklist.Monitors)),
	}

	// 2. Loop over each monitor in sequence
	for i, monitor := range worklist.Monitors {

		result := SingleCheck(monitor)
		fmt.Printf("[%d/%d] %-30s %v %d %v\n", i+1, len(worklist.Monitors), monitor.Name, result.Status, result.HTTPCode, result.Duration)

		// Append result to JobResponse
		jobResponse.Results = append(jobResponse.Results, result)
	}

	// Send response to API
	err = SendResponse(jobResponse)
	fmt.Println("Response sent to API")
	if err != nil {
		return fmt.Errorf("failed to send response: %w", err)
	}

	// duration
	duration := time.Since(start)
	fmt.Printf("Done - duration = %v\n", duration)

	return nil
}
