package main

// Eklenti surec yoneticisi. Bridge yalnizca KOPRUDUR: eklentiyi baslatir, durdurur ve
// stdin/stdout uzerinden satir bazli JSON mesajlarini iletir. Is mantigi eklentidedir.
//   bridge -> eklenti (stdin):  her satir bir JSON mesajidir
//   eklenti -> bridge (stdout): her satir bir JSON mesajidir (cevap ya da olay)
// Surec beklenmedik sekilde biterse yonetici kaydi siler; olay geri cagrisi her satir icin cagrilir.

import (
	"bufio"
	"encoding/json"
	"errors"
	"io"
	"os/exec"
	"sync"
	"syscall"
	"time"
)

const surecDurdurZamanAsimi = 3 * time.Second

var (
	errSurecCalisiyor = errors.New("süreç zaten çalışıyor")
	errSurecYok       = errors.New("süreç çalışmıyor")
)

// surecOlay: eklentiden gelen her satir icin cagrilir (anahtar ve ham JSON satiri).
type surecOlay func(anahtar string, satir []byte)

// surecCikisiGeri: bir surec bittiginde (durdurma ya da beklenmedik cikis) cagrilir; durumu sunucuya yeniden bildirir.
var surecCikisiGeri = func(string) { eklentiDurumGonder() }

type calisanSurec struct {
	cmd   *exec.Cmd
	stdin io.WriteCloser
	bitti chan struct{}
}

type surecYoneticisi struct {
	mu       sync.Mutex
	surecler map[string]*calisanSurec
	olay     surecOlay
}

func yeniSurecYoneticisi(olay surecOlay) *surecYoneticisi {
	return &surecYoneticisi{surecler: map[string]*calisanSurec{}, olay: olay}
}

// Baslat: eklentiyi ayri surec olarak baslatir. Ayni anahtar zaten calisiyorsa hata doner.
func (y *surecYoneticisi) Baslat(anahtar, yol string, args ...string) error {
	y.mu.Lock()
	defer y.mu.Unlock()
	if _, calisiyor := y.surecler[anahtar]; calisiyor {
		return errSurecCalisiyor
	}
	cmd := exec.Command(yol, args...)
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	cmd.Stderr = io.Discard
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	surecIsNesnesineBagla(cmd.Process.Pid)
	s := &calisanSurec{cmd: cmd, stdin: stdin, bitti: make(chan struct{})}
	y.surecler[anahtar] = s

	go func() {
		okuyucu := bufio.NewScanner(stdout)
		okuyucu.Buffer(make([]byte, 64*1024), calistirMaksCikti)
		for okuyucu.Scan() {
			if y.olay != nil {
				y.olay(anahtar, append([]byte(nil), okuyucu.Bytes()...))
			}
		}
		_ = cmd.Wait()
		y.mu.Lock()
		if y.surecler[anahtar] == s {
			delete(y.surecler, anahtar)
		}
		y.mu.Unlock()
		close(s.bitti)
		surecCikisiGeri(anahtar)
	}()
	return nil
}

// Gonder: calisan surece bir JSON mesaji yollar (tek satir).
func (y *surecYoneticisi) Gonder(anahtar string, mesaj any) error {
	y.mu.Lock()
	defer y.mu.Unlock()
	s, ok := y.surecler[anahtar]
	if !ok {
		return errSurecYok
	}
	satir, err := json.Marshal(mesaj)
	if err != nil {
		return err
	}
	_, err = s.stdin.Write(append(satir, '\n'))
	return err
}

// Durdur: sureci sonlandirir. Once girdiyi kapatir, sonra zorla bitirir.
func (y *surecYoneticisi) Durdur(anahtar string) error {
	y.mu.Lock()
	s, ok := y.surecler[anahtar]
	if ok {
		delete(y.surecler, anahtar)
	}
	y.mu.Unlock()
	if !ok {
		return errSurecYok
	}
	_ = s.stdin.Close()
	if s.cmd.Process != nil {
		_ = s.cmd.Process.Kill()
	}
	select {
	case <-s.bitti:
	case <-time.After(surecDurdurZamanAsimi):
	}
	return nil
}

// Anahtarlar: calisan eklenti sureclerinin anahtarlari (kapanista hepsini durdurmak icin).
func (y *surecYoneticisi) Anahtarlar() []string {
	y.mu.Lock()
	defer y.mu.Unlock()
	liste := make([]string, 0, len(y.surecler))
	for a := range y.surecler {
		liste = append(liste, a)
	}
	return liste
}

// Calisiyor: anahtar icin surec var mi.
func (y *surecYoneticisi) Calisiyor(anahtar string) bool {
	y.mu.Lock()
	defer y.mu.Unlock()
	_, ok := y.surecler[anahtar]
	return ok
}
