package handlers

import (
	"bufio"
	"bytes"
	"context"
	"log"
	"os/exec"
	"strings"
	"sync"
	"time"
)

// Aktif komutları takip et (iptal için)
var (
	aktifKomutlar = make(map[string]*exec.Cmd)
	aktifKomutMu  sync.RWMutex
)

// KomutHandler - Komut işleyici
type KomutHandler struct{}

// YeniKomutHandler - Yeni komut handler
func YeniKomutHandler() *KomutHandler {
	return &KomutHandler{}
}

// KomutSonuc - Komut sonucu
type KomutSonuc struct {
	GorevId     string `json:"gorevId"`
	Cikti       string `json:"cikti"`
	HataCikti   string `json:"hataCikti"`
	CikisDurumu int    `json:"cikisDurumu"`
}

// SonucCallback - Sonuç gönderme fonksiyonu
type SonucCallback func(sonuc KomutSonuc)

// CiktiCallback - Streaming çıktı gönderme fonksiyonu
// gorevId: görev ID, satir: çıktı satırı, tip: "stdout" veya "stderr"
type CiktiCallback func(gorevId string, satir string, tip string)

// KomutCalistir - Komut çalıştır
func (kh *KomutHandler) KomutCalistir(data map[string]interface{}, sonucGonder SonucCallback) {
	gorevId, _ := data["gorevId"].(string)
	komut, _ := data["komut"].(string)
	tip, _ := data["tip"].(string)
	calismaDizin, _ := data["calismaDizin"].(string)
	timeoutSaniye, _ := data["timeout"].(float64)

	if timeoutSaniye == 0 {
		timeoutSaniye = 60
	}

	log.Printf("Komut çalıştırılıyor - Tip: %s, Komut: %s", tip, komut)

	// Timeout context
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutSaniye)*time.Second)
	defer cancel()

	var cmd *exec.Cmd

	switch strings.ToLower(tip) {
	case "powershell":
		cmd = exec.CommandContext(ctx, "powershell.exe", "-NoProfile", "-NonInteractive", "-Command", komut)
	default: // cmd
		cmd = exec.CommandContext(ctx, "cmd.exe", "/C", komut)
	}

	// Çalışma dizini
	if calismaDizin != "" {
		cmd.Dir = calismaDizin
	}

	// Stdout ve stderr buffer
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	// Komutu çalıştır
	err := cmd.Run()

	sonuc := KomutSonuc{
		GorevId:     gorevId,
		Cikti:       stdout.String(),
		HataCikti:   stderr.String(),
		CikisDurumu: 0,
	}

	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			sonuc.CikisDurumu = exitErr.ExitCode()
		} else if ctx.Err() == context.DeadlineExceeded {
			sonuc.HataCikti = "Komut timeout süresi doldu"
			sonuc.CikisDurumu = -1
		} else {
			sonuc.HataCikti = err.Error()
			sonuc.CikisDurumu = -1
		}
	}

	log.Printf("Komut tamamlandı - GorevId: %s, CikisDurumu: %d", gorevId, sonuc.CikisDurumu)

	// Sonucu gönder
	if sonucGonder != nil {
		sonucGonder(sonuc)
	}
}

// KomutCalistirStreaming - Streaming çıktı ile komut çalıştır
func (kh *KomutHandler) KomutCalistirStreaming(data map[string]interface{}, ciktiGonder CiktiCallback, sonucGonder SonucCallback) {
	gorevId, _ := data["gorevId"].(string)
	komut, _ := data["komut"].(string)
	tip, _ := data["tip"].(string)
	calismaDizin, _ := data["calismaDizin"].(string)
	timeoutSaniye, _ := data["timeout"].(float64)

	if timeoutSaniye == 0 {
		timeoutSaniye = 60
	}

	log.Printf("Komut çalıştırılıyor (streaming) - Tip: %s, Komut: %s", tip, komut)

	// Timeout context
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutSaniye)*time.Second)
	defer cancel()

	var cmd *exec.Cmd

	switch strings.ToLower(tip) {
	case "powershell":
		cmd = exec.CommandContext(ctx, "powershell.exe", "-NoProfile", "-NonInteractive", "-Command", komut)
	default: // cmd
		cmd = exec.CommandContext(ctx, "cmd.exe", "/C", komut)
	}

	// Çalışma dizini
	if calismaDizin != "" {
		cmd.Dir = calismaDizin
	}

	// Aktif komutlara ekle (iptal için)
	aktifKomutMu.Lock()
	aktifKomutlar[gorevId] = cmd
	aktifKomutMu.Unlock()

	defer func() {
		aktifKomutMu.Lock()
		delete(aktifKomutlar, gorevId)
		aktifKomutMu.Unlock()
	}()

	// Pipe'ları oluştur
	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		log.Printf("stdout pipe hatası: %v", err)
		if sonucGonder != nil {
			sonucGonder(KomutSonuc{
				GorevId:     gorevId,
				HataCikti:   err.Error(),
				CikisDurumu: -1,
			})
		}
		return
	}

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		log.Printf("stderr pipe hatası: %v", err)
		if sonucGonder != nil {
			sonucGonder(KomutSonuc{
				GorevId:     gorevId,
				HataCikti:   err.Error(),
				CikisDurumu: -1,
			})
		}
		return
	}

	// Komutu başlat
	if err := cmd.Start(); err != nil {
		log.Printf("Komut başlatma hatası: %v", err)
		if sonucGonder != nil {
			sonucGonder(KomutSonuc{
				GorevId:     gorevId,
				HataCikti:   err.Error(),
				CikisDurumu: -1,
			})
		}
		return
	}

	// Tam çıktıyı toplamak için buffer
	var fullStdout, fullStderr bytes.Buffer
	var wg sync.WaitGroup

	// Stdout okuyucu
	wg.Add(1)
	go func() {
		defer wg.Done()
		scanner := bufio.NewScanner(stdoutPipe)
		for scanner.Scan() {
			line := scanner.Text()
			fullStdout.WriteString(line + "\n")
			if ciktiGonder != nil {
				ciktiGonder(gorevId, line, "stdout")
			}
		}
	}()

	// Stderr okuyucu
	wg.Add(1)
	go func() {
		defer wg.Done()
		scanner := bufio.NewScanner(stderrPipe)
		for scanner.Scan() {
			line := scanner.Text()
			fullStderr.WriteString(line + "\n")
			if ciktiGonder != nil {
				ciktiGonder(gorevId, line, "stderr")
			}
		}
	}()

	// Okuyucuların bitmesini bekle
	wg.Wait()

	// Komutun bitmesini bekle
	err = cmd.Wait()

	sonuc := KomutSonuc{
		GorevId:     gorevId,
		Cikti:       strings.TrimSpace(fullStdout.String()),
		HataCikti:   strings.TrimSpace(fullStderr.String()),
		CikisDurumu: 0,
	}

	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			sonuc.CikisDurumu = exitErr.ExitCode()
		} else if ctx.Err() == context.DeadlineExceeded {
			sonuc.HataCikti = "Komut timeout süresi doldu"
			sonuc.CikisDurumu = -1
		} else {
			sonuc.HataCikti = err.Error()
			sonuc.CikisDurumu = -1
		}
	}

	log.Printf("Komut tamamlandı (streaming) - GorevId: %s, CikisDurumu: %d", gorevId, sonuc.CikisDurumu)

	// Sonucu gönder
	if sonucGonder != nil {
		sonucGonder(sonuc)
	}
}

// KomutIptal - Çalışan komutu iptal et
func (kh *KomutHandler) KomutIptal(gorevId string) bool {
	aktifKomutMu.RLock()
	cmd, ok := aktifKomutlar[gorevId]
	aktifKomutMu.RUnlock()

	if !ok || cmd == nil || cmd.Process == nil {
		log.Printf("İptal edilecek komut bulunamadı: %s", gorevId)
		return false
	}

	log.Printf("Komut iptal ediliyor: %s", gorevId)

	if err := cmd.Process.Kill(); err != nil {
		log.Printf("Komut iptal hatası: %v", err)
		return false
	}

	aktifKomutMu.Lock()
	delete(aktifKomutlar, gorevId)
	aktifKomutMu.Unlock()

	log.Printf("Komut iptal edildi: %s", gorevId)
	return true
}

// ProgramAc - Program veya dosya aç
func (kh *KomutHandler) ProgramAc(data map[string]interface{}) error {
	yol, _ := data["yol"].(string)

	log.Printf("Program açılıyor: %s", yol)

	cmd := exec.Command("cmd.exe", "/C", "start", "", yol)
	return cmd.Start()
}

// URLAc - URL aç
func (kh *KomutHandler) URLAc(url string) error {
	log.Printf("URL açılıyor: %s", url)

	cmd := exec.Command("cmd.exe", "/C", "start", "", url)
	return cmd.Start()
}
