package processmanager

import (
	"fmt"
	"log"
	"os/exec"
	"runtime"
	"sort"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/shirou/gopsutil/v3/cpu"
	"github.com/shirou/gopsutil/v3/process"
)

// ProcessManager - Süreç yönetim servisi
type ProcessManager struct {
	mu              sync.RWMutex
	watchedProcesses map[int32]*WatchedProcess
	processCache    []ProcessInfo
	lastCacheTime   time.Time
	cacheTimeout    time.Duration
}

// ProcessInfo - Süreç bilgisi
type ProcessInfo struct {
	PID         int32     `json:"pid"`
	PPID        int32     `json:"ppid"`
	Name        string    `json:"name"`
	Exe         string    `json:"exe"`
	Cmdline     string    `json:"cmdline"`
	Cwd         string    `json:"cwd"`
	Status      string    `json:"status"`
	Username    string    `json:"username"`
	CreateTime  time.Time `json:"createTime"`
	CPUPercent  float64   `json:"cpuPercent"`
	MemPercent  float32   `json:"memPercent"`
	MemRSS      uint64    `json:"memRSS"`      // Resident Set Size
	MemVMS      uint64    `json:"memVMS"`      // Virtual Memory Size
	NumThreads  int32     `json:"numThreads"`
	NumFDs      int32     `json:"numFDs"`      // File descriptors
	IOReadBytes  uint64   `json:"ioReadBytes"`
	IOWriteBytes uint64   `json:"ioWriteBytes"`
	Connections  int      `json:"connections"` // Network bağlantı sayısı
	Priority    int32     `json:"priority"`
	Nice        int32     `json:"nice"`
	IsSystem    bool      `json:"isSystem"`
	Children    []int32   `json:"children,omitempty"`
}

// ProcessTree - Süreç ağacı
type ProcessTree struct {
	Process  ProcessInfo    `json:"process"`
	Children []ProcessTree  `json:"children,omitempty"`
}

// WatchedProcess - İzlenen süreç
type WatchedProcess struct {
	PID        int32
	Name       string
	StartTime  time.Time
	History    []ProcessSnapshot
	MaxHistory int
}

// ProcessSnapshot - Süreç anlık durumu
type ProcessSnapshot struct {
	Time       time.Time `json:"time"`
	CPUPercent float64   `json:"cpuPercent"`
	MemPercent float32   `json:"memPercent"`
	MemRSS     uint64    `json:"memRSS"`
}

// SystemProcessStats - Sistem süreç istatistikleri
type SystemProcessStats struct {
	TotalProcesses  int     `json:"totalProcesses"`
	RunningCount    int     `json:"runningCount"`
	SleepingCount   int     `json:"sleepingCount"`
	StoppedCount    int     `json:"stoppedCount"`
	ZombieCount     int     `json:"zombieCount"`
	TotalCPUPercent float64 `json:"totalCpuPercent"`
	TotalMemPercent float64 `json:"totalMemPercent"`
	TopCPUProcesses []ProcessInfo `json:"topCpuProcesses"`
	TopMemProcesses []ProcessInfo `json:"topMemProcesses"`
}

// NewProcessManager - Yeni süreç yöneticisi
func NewProcessManager() *ProcessManager {
	return &ProcessManager{
		watchedProcesses: make(map[int32]*WatchedProcess),
		cacheTimeout:     2 * time.Second,
	}
}

// GetAllProcesses - Tüm süreçleri listele
func (pm *ProcessManager) GetAllProcesses(sortBy string, sortDesc bool, filter string) ([]ProcessInfo, error) {
	processes, err := pm.fetchProcesses()
	if err != nil {
		return nil, err
	}

	// Filtrele
	if filter != "" {
		filter = strings.ToLower(filter)
		filtered := make([]ProcessInfo, 0)
		for _, p := range processes {
			if strings.Contains(strings.ToLower(p.Name), filter) ||
				strings.Contains(strings.ToLower(p.Cmdline), filter) ||
				strings.Contains(strings.ToLower(p.Username), filter) {
				filtered = append(filtered, p)
			}
		}
		processes = filtered
	}

	// Sırala
	pm.sortProcesses(processes, sortBy, sortDesc)

	return processes, nil
}

// fetchProcesses - Süreçleri getir (cache'li)
func (pm *ProcessManager) fetchProcesses() ([]ProcessInfo, error) {
	pm.mu.RLock()
	if time.Since(pm.lastCacheTime) < pm.cacheTimeout && len(pm.processCache) > 0 {
		cache := pm.processCache
		pm.mu.RUnlock()
		return cache, nil
	}
	pm.mu.RUnlock()

	procs, err := process.Processes()
	if err != nil {
		return nil, err
	}

	processes := make([]ProcessInfo, 0, len(procs))

	for _, p := range procs {
		info := pm.getProcessInfo(p)
		if info != nil {
			processes = append(processes, *info)
		}
	}

	pm.mu.Lock()
	pm.processCache = processes
	pm.lastCacheTime = time.Now()
	pm.mu.Unlock()

	return processes, nil
}

// getProcessInfo - Tek süreç bilgisi
func (pm *ProcessManager) getProcessInfo(p *process.Process) *ProcessInfo {
	info := &ProcessInfo{
		PID: p.Pid,
	}

	// Temel bilgiler
	if name, err := p.Name(); err == nil {
		info.Name = name
	}

	if ppid, err := p.Ppid(); err == nil {
		info.PPID = ppid
	}

	if exe, err := p.Exe(); err == nil {
		info.Exe = exe
	}

	if cmdline, err := p.Cmdline(); err == nil {
		info.Cmdline = cmdline
	}

	if cwd, err := p.Cwd(); err == nil {
		info.Cwd = cwd
	}

	if status, err := p.Status(); err == nil && len(status) > 0 {
		info.Status = status[0]
	}

	if username, err := p.Username(); err == nil {
		info.Username = username
	}

	if createTime, err := p.CreateTime(); err == nil {
		info.CreateTime = time.Unix(createTime/1000, 0)
	}

	// Performans metrikleri
	if cpuPercent, err := p.CPUPercent(); err == nil {
		info.CPUPercent = cpuPercent
	}

	if memPercent, err := p.MemoryPercent(); err == nil {
		info.MemPercent = memPercent
	}

	if memInfo, err := p.MemoryInfo(); err == nil && memInfo != nil {
		info.MemRSS = memInfo.RSS
		info.MemVMS = memInfo.VMS
	}

	if threads, err := p.NumThreads(); err == nil {
		info.NumThreads = threads
	}

	if fds, err := p.NumFDs(); err == nil {
		info.NumFDs = fds
	}

	if ioCounters, err := p.IOCounters(); err == nil && ioCounters != nil {
		info.IOReadBytes = ioCounters.ReadBytes
		info.IOWriteBytes = ioCounters.WriteBytes
	}

	if conns, err := p.Connections(); err == nil {
		info.Connections = len(conns)
	}

	if nice, err := p.Nice(); err == nil {
		info.Nice = nice
	}

	// Sistem süreç kontrolü
	info.IsSystem = isSystemProcess(info.Name, info.Username)

	// Alt süreçler
	if children, err := p.Children(); err == nil {
		for _, child := range children {
			info.Children = append(info.Children, child.Pid)
		}
	}

	return info
}

// GetProcess - Tek süreç detayı
func (pm *ProcessManager) GetProcess(pid int32) (*ProcessInfo, error) {
	p, err := process.NewProcess(pid)
	if err != nil {
		return nil, fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	info := pm.getProcessInfo(p)
	if info == nil {
		return nil, fmt.Errorf("süreç bilgisi alınamadı: %d", pid)
	}

	return info, nil
}

// GetProcessTree - Süreç ağacı
func (pm *ProcessManager) GetProcessTree() ([]ProcessTree, error) {
	processes, err := pm.fetchProcesses()
	if err != nil {
		return nil, err
	}

	// PID'e göre map oluştur
	processMap := make(map[int32]ProcessInfo)
	childrenMap := make(map[int32][]int32)

	for _, p := range processes {
		processMap[p.PID] = p
		childrenMap[p.PPID] = append(childrenMap[p.PPID], p.PID)
	}

	// Kök süreçleri bul (PPID = 0 veya PPID listede yok)
	var roots []ProcessTree
	for _, p := range processes {
		if p.PPID == 0 || processMap[p.PPID].PID == 0 {
			tree := pm.buildProcessTree(p, processMap, childrenMap)
			roots = append(roots, tree)
		}
	}

	return roots, nil
}

// buildProcessTree - Süreç ağacını oluştur
func (pm *ProcessManager) buildProcessTree(p ProcessInfo, processMap map[int32]ProcessInfo, childrenMap map[int32][]int32) ProcessTree {
	tree := ProcessTree{
		Process: p,
	}

	if children, ok := childrenMap[p.PID]; ok {
		for _, childPID := range children {
			if childProcess, ok := processMap[childPID]; ok {
				childTree := pm.buildProcessTree(childProcess, processMap, childrenMap)
				tree.Children = append(tree.Children, childTree)
			}
		}
	}

	return tree
}

// KillProcess - Süreç sonlandır
func (pm *ProcessManager) KillProcess(pid int32, force bool) error {
	p, err := process.NewProcess(pid)
	if err != nil {
		return fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	// Sistem süreç koruması
	if info := pm.getProcessInfo(p); info != nil && info.IsSystem {
		return fmt.Errorf("sistem süreçleri sonlandırılamaz: %s", info.Name)
	}

	if force {
		return p.Kill()
	}
	return p.Terminate()
}

// KillProcessTree - Süreç ve alt süreçlerini sonlandır
func (pm *ProcessManager) KillProcessTree(pid int32, force bool) error {
	p, err := process.NewProcess(pid)
	if err != nil {
		return fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	// Alt süreçleri bul ve sonlandır
	children, _ := p.Children()
	for _, child := range children {
		pm.KillProcessTree(child.Pid, force)
	}

	// Ana süreci sonlandır
	return pm.KillProcess(pid, force)
}

// SuspendProcess - Süreç duraklat
func (pm *ProcessManager) SuspendProcess(pid int32) error {
	p, err := process.NewProcess(pid)
	if err != nil {
		return fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	return p.Suspend()
}

// ResumeProcess - Süreç devam ettir
func (pm *ProcessManager) ResumeProcess(pid int32) error {
	p, err := process.NewProcess(pid)
	if err != nil {
		return fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	return p.Resume()
}

// SetProcessPriority - Süreç önceliği ayarla
func (pm *ProcessManager) SetProcessPriority(pid int32, priority int32) error {
	_, err := process.NewProcess(pid)
	if err != nil {
		return fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	// Windows için öncelik değerleri
	if runtime.GOOS == "windows" {
		return setWindowsPriority(pid, priority)
	}

	// Linux/macOS için nice değeri - şu an desteklenmiyor
	return fmt.Errorf("öncelik ayarlama bu platformda desteklenmiyor")
}

// StartProcess - Yeni süreç başlat
func (pm *ProcessManager) StartProcess(path string, args []string, cwd string, env map[string]string) (int32, error) {
	cmd := exec.Command(path, args...)

	if cwd != "" {
		cmd.Dir = cwd
	}

	if len(env) > 0 {
		for k, v := range env {
			cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
		}
	}

	// Detached process (Windows)
	if runtime.GOOS == "windows" {
		cmd.SysProcAttr = &syscall.SysProcAttr{
			CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
		}
	}

	err := cmd.Start()
	if err != nil {
		return 0, err
	}

	return int32(cmd.Process.Pid), nil
}

// WatchProcess - Süreç izlemeye al
func (pm *ProcessManager) WatchProcess(pid int32) error {
	p, err := process.NewProcess(pid)
	if err != nil {
		return fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	name, _ := p.Name()

	pm.mu.Lock()
	pm.watchedProcesses[pid] = &WatchedProcess{
		PID:        pid,
		Name:       name,
		StartTime:  time.Now(),
		MaxHistory: 60, // 60 snapshot
		History:    make([]ProcessSnapshot, 0),
	}
	pm.mu.Unlock()

	log.Printf("Süreç izlemeye alındı: %d (%s)", pid, name)
	return nil
}

// UnwatchProcess - Süreç izlemeden çıkar
func (pm *ProcessManager) UnwatchProcess(pid int32) {
	pm.mu.Lock()
	delete(pm.watchedProcesses, pid)
	pm.mu.Unlock()

	log.Printf("Süreç izlemeden çıkarıldı: %d", pid)
}

// GetWatchedProcesses - İzlenen süreçleri getir
func (pm *ProcessManager) GetWatchedProcesses() map[int32]*WatchedProcess {
	pm.mu.RLock()
	defer pm.mu.RUnlock()

	result := make(map[int32]*WatchedProcess)
	for k, v := range pm.watchedProcesses {
		result[k] = v
	}
	return result
}

// UpdateWatchedProcesses - İzlenen süreçleri güncelle
func (pm *ProcessManager) UpdateWatchedProcesses() {
	pm.mu.Lock()
	defer pm.mu.Unlock()

	for pid, watched := range pm.watchedProcesses {
		p, err := process.NewProcess(pid)
		if err != nil {
			// Süreç artık yok
			delete(pm.watchedProcesses, pid)
			continue
		}

		cpuPercent, _ := p.CPUPercent()
		memPercent, _ := p.MemoryPercent()
		memInfo, _ := p.MemoryInfo()

		var memRSS uint64
		if memInfo != nil {
			memRSS = memInfo.RSS
		}

		snapshot := ProcessSnapshot{
			Time:       time.Now(),
			CPUPercent: cpuPercent,
			MemPercent: memPercent,
			MemRSS:     memRSS,
		}

		watched.History = append(watched.History, snapshot)

		// Tarihçe sınırı
		if len(watched.History) > watched.MaxHistory {
			watched.History = watched.History[1:]
		}
	}
}

// GetSystemProcessStats - Sistem süreç istatistikleri
func (pm *ProcessManager) GetSystemProcessStats() (*SystemProcessStats, error) {
	processes, err := pm.fetchProcesses()
	if err != nil {
		return nil, err
	}

	stats := &SystemProcessStats{
		TotalProcesses: len(processes),
	}

	for _, p := range processes {
		stats.TotalCPUPercent += p.CPUPercent
		stats.TotalMemPercent += float64(p.MemPercent)

		switch p.Status {
		case "R", "running":
			stats.RunningCount++
		case "S", "sleeping":
			stats.SleepingCount++
		case "T", "stopped":
			stats.StoppedCount++
		case "Z", "zombie":
			stats.ZombieCount++
		}
	}

	// Top 5 CPU kullanan
	sortedByCPU := make([]ProcessInfo, len(processes))
	copy(sortedByCPU, processes)
	sort.Slice(sortedByCPU, func(i, j int) bool {
		return sortedByCPU[i].CPUPercent > sortedByCPU[j].CPUPercent
	})
	if len(sortedByCPU) > 5 {
		stats.TopCPUProcesses = sortedByCPU[:5]
	} else {
		stats.TopCPUProcesses = sortedByCPU
	}

	// Top 5 Memory kullanan
	sortedByMem := make([]ProcessInfo, len(processes))
	copy(sortedByMem, processes)
	sort.Slice(sortedByMem, func(i, j int) bool {
		return sortedByMem[i].MemPercent > sortedByMem[j].MemPercent
	})
	if len(sortedByMem) > 5 {
		stats.TopMemProcesses = sortedByMem[:5]
	} else {
		stats.TopMemProcesses = sortedByMem
	}

	return stats, nil
}

// GetCPUUsageHistory - CPU kullanım geçmişi
func (pm *ProcessManager) GetCPUUsageHistory(duration time.Duration, interval time.Duration) ([]float64, error) {
	samples := int(duration / interval)
	history := make([]float64, 0, samples)

	for i := 0; i < samples; i++ {
		percents, err := cpu.Percent(interval, false)
		if err != nil {
			return history, err
		}
		if len(percents) > 0 {
			history = append(history, percents[0])
		}
	}

	return history, nil
}

// sortProcesses - Süreçleri sırala
func (pm *ProcessManager) sortProcesses(processes []ProcessInfo, sortBy string, sortDesc bool) {
	sort.Slice(processes, func(i, j int) bool {
		var result bool
		switch sortBy {
		case "cpu":
			result = processes[i].CPUPercent < processes[j].CPUPercent
		case "memory", "mem":
			result = processes[i].MemPercent < processes[j].MemPercent
		case "pid":
			result = processes[i].PID < processes[j].PID
		case "user", "username":
			result = processes[i].Username < processes[j].Username
		case "threads":
			result = processes[i].NumThreads < processes[j].NumThreads
		default: // "name"
			result = strings.ToLower(processes[i].Name) < strings.ToLower(processes[j].Name)
		}

		if sortDesc {
			return !result
		}
		return result
	})
}

// isSystemProcess - Sistem süreci mi
func isSystemProcess(name, username string) bool {
	systemProcesses := []string{
		"System", "smss.exe", "csrss.exe", "wininit.exe",
		"services.exe", "lsass.exe", "svchost.exe", "winlogon.exe",
		"dwm.exe", "fontdrvhost.exe", "audiodg.exe",
		"init", "systemd", "kthreadd", "kworker",
	}

	nameLower := strings.ToLower(name)
	for _, sys := range systemProcesses {
		if strings.ToLower(sys) == nameLower {
			return true
		}
	}

	// Sistem kullanıcıları
	systemUsers := []string{
		"SYSTEM", "LOCAL SERVICE", "NETWORK SERVICE",
		"root", "daemon", "nobody",
	}

	for _, user := range systemUsers {
		if strings.EqualFold(user, username) {
			return true
		}
	}

	return false
}

// setWindowsPriority - Windows öncelik ayarla
func setWindowsPriority(pid int32, priority int32) error {
	// Windows API kullanımı gerektirir
	// Basitleştirilmiş versiyon - sadece nice değeri ile
	return nil
}

// SearchProcessByPort - Port'a göre süreç bul
func (pm *ProcessManager) SearchProcessByPort(port uint32) (*ProcessInfo, error) {
	procs, err := process.Processes()
	if err != nil {
		return nil, err
	}

	for _, p := range procs {
		conns, err := p.Connections()
		if err != nil {
			continue
		}

		for _, conn := range conns {
			if conn.Laddr.Port == port {
				info := pm.getProcessInfo(p)
				return info, nil
			}
		}
	}

	return nil, fmt.Errorf("port %d kullanan süreç bulunamadı", port)
}

// GetProcessConnections - Süreç ağ bağlantıları
func (pm *ProcessManager) GetProcessConnections(pid int32) ([]ConnectionInfo, error) {
	p, err := process.NewProcess(pid)
	if err != nil {
		return nil, fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	conns, err := p.Connections()
	if err != nil {
		return nil, err
	}

	var connections []ConnectionInfo
	for _, conn := range conns {
		connections = append(connections, ConnectionInfo{
			FD:         conn.Fd,
			Family:     conn.Family,
			Type:       conn.Type,
			LocalAddr:  conn.Laddr.IP,
			LocalPort:  conn.Laddr.Port,
			RemoteAddr: conn.Raddr.IP,
			RemotePort: conn.Raddr.Port,
			Status:     conn.Status,
		})
	}

	return connections, nil
}

// ConnectionInfo - Ağ bağlantı bilgisi
type ConnectionInfo struct {
	FD         uint32 `json:"fd"`
	Family     uint32 `json:"family"`     // AF_INET, AF_INET6
	Type       uint32 `json:"type"`       // SOCK_STREAM, SOCK_DGRAM
	LocalAddr  string `json:"localAddr"`
	LocalPort  uint32 `json:"localPort"`
	RemoteAddr string `json:"remoteAddr"`
	RemotePort uint32 `json:"remotePort"`
	Status     string `json:"status"`
}

// GetProcessOpenFiles - Süreç açık dosyaları
func (pm *ProcessManager) GetProcessOpenFiles(pid int32) ([]OpenFileInfo, error) {
	p, err := process.NewProcess(pid)
	if err != nil {
		return nil, fmt.Errorf("süreç bulunamadı: %d", pid)
	}

	files, err := p.OpenFiles()
	if err != nil {
		return nil, err
	}

	var openFiles []OpenFileInfo
	for _, f := range files {
		openFiles = append(openFiles, OpenFileInfo{
			Path: f.Path,
			FD:   f.Fd,
		})
	}

	return openFiles, nil
}

// OpenFileInfo - Açık dosya bilgisi
type OpenFileInfo struct {
	Path string `json:"path"`
	FD   uint64 `json:"fd"`
}
