package scheduler

import (
	"log"
	"medi-wol/internal/database"
	"medi-wol/internal/models"
	"medi-wol/internal/wol"
	"strconv"
	"strings"
	"time"
)

// Scheduler verwaltet geplante WOL-Ereignisse
type Scheduler struct {
	db         *database.DB
	wolService *wol.Service
	ticker     *time.Ticker
	stopChan   chan bool
}

// NewScheduler erstellt einen neuen Scheduler
func NewScheduler(db *database.DB, wolService *wol.Service) *Scheduler {
	return &Scheduler{
		db:         db,
		wolService: wolService,
		stopChan:   make(chan bool),
	}
}

// Start startet den Scheduler
func (s *Scheduler) Start() {
	s.ticker = time.NewTicker(1 * time.Minute) // Jede Minute prüfen
	go s.run()
	log.Println("Autostart-Scheduler gestartet")
}

// Stop stoppt den Scheduler
func (s *Scheduler) Stop() {
	if s.ticker != nil {
		s.ticker.Stop()
	}
	close(s.stopChan)
	log.Println("Autostart-Scheduler gestoppt")
}

// run ist die Hauptschleife des Schedulers
func (s *Scheduler) run() {
	for {
		select {
		case <-s.ticker.C:
			s.checkAndExecuteScheduledTasks()
		case <-s.stopChan:
			return
		}
	}
}

// checkAndExecuteScheduledTasks prüft alle geplanten Aufgaben
func (s *Scheduler) checkAndExecuteScheduledTasks() {
	now := time.Now()

	// Alle PCs mit aktiviertem Autostart holen
	pcs, err := s.db.GetPCsWithAutostart()
	if err != nil {
		log.Printf("Fehler beim Laden der PCs mit Autostart: %v", err)
		return
	}

	for _, pc := range pcs {
		if s.shouldExecuteNow(pc.AutostartCron, now) {
			s.executeWOL(pc)
		}
	}
}

// shouldExecuteNow prüft, ob ein Crontab-Ausdruck jetzt ausgeführt werden soll
func (s *Scheduler) shouldExecuteNow(cronExpr string, now time.Time) bool {
	parts := strings.Fields(cronExpr)
	if len(parts) != 5 {
		log.Printf("Ungültiger Crontab-Ausdruck: %s", cronExpr)
		return false
	}

	// Einfache Crontab-Parser-Implementierung
	// Format: Minute Stunde Tag Monat Wochentag
	minute := s.parseCronField(parts[0], 0, 59, now.Minute())
	hour := s.parseCronField(parts[1], 0, 23, now.Hour())
	day := s.parseCronField(parts[2], 1, 31, now.Day())
	month := s.parseCronField(parts[3], 1, 12, int(now.Month()))
	weekday := s.parseCronField(parts[4], 0, 6, int(now.Weekday()))

	return minute && hour && day && month && weekday
}

// parseCronField parst ein einzelnes Crontab-Feld
func (s *Scheduler) parseCronField(field string, min, max, current int) bool {
	// Einfache Implementierung für häufige Fälle
	if field == "*" {
		return true
	}

	// Einzelne Zahl
	if num, err := strconv.Atoi(field); err == nil {
		return num == current
	}

	// Bereich (z.B. "1-5")
	if strings.Contains(field, "-") {
		parts := strings.Split(field, "-")
		if len(parts) == 2 {
			start, err1 := strconv.Atoi(parts[0])
			end, err2 := strconv.Atoi(parts[1])
			if err1 == nil && err2 == nil {
				return current >= start && current <= end
			}
		}
	}

	// Wochentag-Namen (z.B. "Mon-Fri")
	if strings.Contains(field, "-") && (strings.Contains(field, "Mon") || strings.Contains(field, "Tue") ||
		strings.Contains(field, "Wed") || strings.Contains(field, "Thu") ||
		strings.Contains(field, "Fri") || strings.Contains(field, "Sat") || strings.Contains(field, "Sun")) {
		return s.parseWeekdayRange(field, current)
	}

	return false
}

// parseWeekdayRange parst Wochentag-Bereiche
func (s *Scheduler) parseWeekdayRange(field string, current int) bool {
	// Wochentag-Mapping (0=Sonntag, 1=Montag, ..., 6=Samstag)
	weekdayMap := map[string]int{
		"Sun": 0, "Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6,
	}

	if strings.Contains(field, "-") {
		parts := strings.Split(field, "-")
		if len(parts) == 2 {
			start, ok1 := weekdayMap[parts[0]]
			end, ok2 := weekdayMap[parts[1]]
			if ok1 && ok2 {
				// Spezielle Behandlung für Wochentag-Bereiche
				if start <= end {
					return current >= start && current <= end
				} else {
					// Bereich über Mitternacht (z.B. Fri-Mon)
					return current >= start || current <= end
				}
			}
		}
	}

	return false
}

// executeWOL führt einen geplanten WOL-Befehl aus
func (s *Scheduler) executeWOL(pc models.PC) {
	log.Printf("Führe geplanten WOL für PC %s (%s) aus", pc.Name, pc.MAC)

	// WOL-Paket senden
	err := s.wolService.WakePC(pc.MAC)
	if err != nil {
		log.Printf("Fehler beim Senden des WOL-Pakets für PC %s: %v", pc.Name, err)
		return
	}

	// Log-Eintrag erstellen
	_, logErr := s.db.CreateLog(pc.ID, pc.Name, pc.MAC, "cron")
	if logErr != nil {
		log.Printf("Fehler beim Erstellen des Log-Eintrags: %v", logErr)
	}

	log.Printf("Geplanter WOL für PC %s erfolgreich ausgeführt", pc.Name)
}
