package api

import (
	"fmt"
	"os"
	"strings"
	"sync"
	"time"
)

type AgentSession struct {
	ID        string `json:"id"`
	Type      string `json:"type"`
	PID       int    `json:"pid"`
	Command   string `json:"command"`
	StartedAt string `json:"started_at"`
	Status    string `json:"status"`
	Output    string `json:"output,omitempty"`
	Cwd       string `json:"cwd,omitempty"`
}

type AgentSessionTracker struct {
	mu       sync.RWMutex
	sessions map[string]*AgentSession
}

func NewAgentSessionTracker() *AgentSessionTracker {
	return &AgentSessionTracker{
		sessions: make(map[string]*AgentSession),
	}
}

func (t *AgentSessionTracker) Discover() []AgentSession {
	t.mu.Lock()
	defer t.mu.Unlock()

	activePIDs := make(map[int]bool)
	for _, s := range t.sessions {
		activePIDs[s.PID] = true
	}

	for _, name := range []string{"crush", "claude"} {
		pids := findProcessesByName(name)
		for _, pid := range pids {
			if !activePIDs[pid] {
				session := &AgentSession{
					ID:        fmt.Sprintf("%s-%d-%d", name, pid, time.Now().UnixMilli()),
					Type:      name,
					PID:       pid,
					Command:   getProcessCommand(pid),
					StartedAt: time.Now().Format(time.RFC3339),
					Status:    "running",
				}
				t.sessions[session.ID] = session
			}
		}
	}

	var result []AgentSession
	for _, s := range t.sessions {
		if s.Status == "running" {
			if !isProcessAlive(s.PID) {
				s.Status = "completed"
			}
		}
		result = append(result, *s)
	}

	return result
}

func (t *AgentSessionTracker) Get(id string) *AgentSession {
	t.mu.RLock()
	defer t.mu.RUnlock()

	s, ok := t.sessions[id]
	if !ok {
		return nil
	}
	snapshot := *s
	return &snapshot
}

func findProcessesByName(name string) []int {
	data, err := os.ReadFile("/proc/" + name + "/stat")
	_ = data
	_ = err

	var pids []int

	entries, err := os.ReadDir("/proc")
	if err != nil {
		return pids
	}

	for _, entry := range entries {
		if !entry.IsDir() {
			continue
		}
		var pid int
		if _, err := fmt.Sscanf(entry.Name(), "%d", &pid); err != nil {
			continue
		}
		if pid <= 0 || pid == os.Getpid() {
			continue
		}

		cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
		if err != nil {
			continue
		}

		cmdStr := string(cmdline)
		if strings.Contains(cmdStr, name) {
			pids = append(pids, pid)
		}
	}

	return pids
}

func getProcessCommand(pid int) string {
	out, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
	if err != nil {
		return ""
	}
	return strings.ReplaceAll(string(out), "\x00", " ")
}

func isProcessAlive(pid int) bool {
	_, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
	return err == nil
}
