package store

import (
	"errors"
	"log/slog"
	"sync"
	"time"

	"nerd-monitor/internal/stats"
)

var (
	// ErrAgentNotFound is returned when an agent is not found.
	ErrAgentNotFound = errors.New("agent not found")
)

// AgentStats represents the latest stats for an agent.
type AgentStats struct {
	ID        string
	Hostname  string
	CPUUsage  float64
	RAMUsage  uint64
	RAMTotal  uint64
	DiskUsage uint64
	DiskTotal uint64
	LastSeen  time.Time
}

// Store manages agent statistics in memory.
type Store struct {
	mu     sync.RWMutex
	agents map[string]*AgentStats
}

// New creates a new store.
func New() *Store {
	return &Store{
		agents: make(map[string]*AgentStats),
	}
}

// UpdateAgent updates or creates agent stats.
func (s *Store) UpdateAgent(id string, stat *stats.Stats) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.agents[id] = &AgentStats{
		ID:        id,
		Hostname:  stat.Hostname,
		CPUUsage:  stat.CPUUsage,
		RAMUsage:  stat.RAMUsage,
		RAMTotal:  stat.RAMTotal,
		DiskUsage: stat.DiskUsage,
		DiskTotal: stat.DiskTotal,
		LastSeen:  time.Now(),
	}
	slog.Debug("Agent stats updated", "agentID", id, "hostname", stat.Hostname, "cpu", stat.CPUUsage)
}

// GetAgent retrieves agent stats by ID.
func (s *Store) GetAgent(id string) *AgentStats {
	s.mu.RLock()
	defer s.mu.RUnlock()

	agent := s.agents[id]
	if agent != nil {
		slog.Debug("Agent retrieved", "agentID", id, "hostname", agent.Hostname)
	} else {
		slog.Debug("Agent not found", "agentID", id)
	}
	return agent
}

// GetAllAgents returns all agents.
func (s *Store) GetAllAgents() []*AgentStats {
	s.mu.RLock()
	defer s.mu.RUnlock()

	agents := make([]*AgentStats, 0, len(s.agents))
	for _, agent := range s.agents {
		agents = append(agents, agent)
	}
	slog.Debug("All agents retrieved", "count", len(agents))
	return agents
}

// DeleteAgent removes an agent.
func (s *Store) DeleteAgent(id string) {
	s.mu.Lock()
	defer s.mu.Unlock()

	delete(s.agents, id)
	slog.Debug("Agent deleted", "agentID", id)
}

// UpdateHostname updates the hostname for an agent.
func (s *Store) UpdateHostname(id string, hostname string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	agent, exists := s.agents[id]
	if !exists {
		slog.Debug("Agent not found for hostname update", "agentID", id)
		return ErrAgentNotFound
	}

	agent.Hostname = hostname
	slog.Debug("Agent hostname updated", "agentID", id, "hostname", hostname)
	return nil
}
