// Package store provides thread-safe in-memory data stores for the
// Control Center API. These will be replaced with PostgreSQL-backed
// implementations once CUB-120 (schema design) is complete.
package store

import (
	"sort"
	"sync"
	"time"

	"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/models"
	"github.com/google/uuid"
)

// AgentStore provides thread-safe CRUD operations for agents.
type AgentStore struct {
	mu      sync.RWMutex
	agents  map[string]models.AgentCardData
	history map[string][]models.AgentStatusHistoryEntry // agentID -> history
}

// NewAgentStore returns an initialized AgentStore.
func NewAgentStore() *AgentStore {
	return &AgentStore{
		agents:  make(map[string]models.AgentCardData),
		history: make(map[string][]models.AgentStatusHistoryEntry),
	}
}

// List returns all agents, optionally filtered by status.
func (s *AgentStore) List(statusFilter models.AgentStatus) []models.AgentCardData {
	s.mu.RLock()
	defer s.mu.RUnlock()

	result := make([]models.AgentCardData, 0, len(s.agents))
	for _, a := range s.agents {
		if statusFilter != "" && a.Status != statusFilter {
			continue
		}
		result = append(result, a)
	}

	// Sort by display name for consistent output.
	sort.Slice(result, func(i, j int) bool {
		return result[i].DisplayName < result[j].DisplayName
	})
	return result
}

// Get returns a single agent by ID, or false if not found.
func (s *AgentStore) Get(id string) (models.AgentCardData, bool) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	a, ok := s.agents[id]
	return a, ok
}

// Create inserts a new agent. Returns false if the ID already exists.
func (s *AgentStore) Create(a models.AgentCardData) bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, exists := s.agents[a.ID]; exists {
		return false
	}
	if a.LastActivity == "" {
		a.LastActivity = time.Now().UTC().Format(time.RFC3339)
	}
	s.agents[a.ID] = a

	// Record initial history entry.
	s.appendHistoryLocked(a.ID, models.AgentStatusHistoryEntry{
		ID:        uuid.New().String(),
		AgentID:   a.ID,
		Status:    a.Status,
		Task:      a.CurrentTask,
		Timestamp: a.LastActivity,
	})
	return true
}

// Update applies partial updates to an agent. Returns the updated agent or false if not found.
func (s *AgentStore) Update(id string, req models.UpdateAgentRequest) (models.AgentCardData, bool) {
	s.mu.Lock()
	defer s.mu.Unlock()

	a, ok := s.agents[id]
	if !ok {
		return models.AgentCardData{}, false
	}

	prevStatus := a.Status
	prevTask := a.CurrentTask

	if req.Status != nil {
		a.Status = *req.Status
	}
	if req.CurrentTask != nil {
		a.CurrentTask = req.CurrentTask
	}
	if req.TaskProgress != nil {
		a.TaskProgress = req.TaskProgress
	}
	if req.TaskElapsed != nil {
		a.TaskElapsed = req.TaskElapsed
	}
	if req.Channel != nil {
		a.Channel = *req.Channel
	}
	if req.ErrorMessage != nil {
		a.ErrorMessage = req.ErrorMessage
	}
	a.LastActivity = time.Now().UTC().Format(time.RFC3339)
	s.agents[id] = a

	// Record history entry if status or task changed.
	if (req.Status != nil && *req.Status != prevStatus) || (req.CurrentTask != nil && prevTask == nil) ||
		(req.CurrentTask != nil && prevTask != nil && *req.CurrentTask != *prevTask) {
		status := a.Status
		if req.Status != nil {
			status = *req.Status
		}
		s.appendHistoryLocked(id, models.AgentStatusHistoryEntry{
			ID:        uuid.New().String(),
			AgentID:   id,
			Status:    status,
			Task:      a.CurrentTask,
			Timestamp: a.LastActivity,
		})
	}

	return a, true
}

// Delete removes an agent. Returns true if the agent existed.
func (s *AgentStore) Delete(id string) bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.agents[id]; !ok {
		return false
	}
	delete(s.agents, id)
	delete(s.history, id)
	return true
}

// History returns the status history for an agent, newest first.
func (s *AgentStore) History(agentID string) []models.AgentStatusHistoryEntry {
	s.mu.RLock()
	defer s.mu.RUnlock()

	entries, ok := s.history[agentID]
	if !ok {
		return nil
	}
	// Return a defensive copy, sorted newest first (by index when timestamps tie).
	result := make([]models.AgentStatusHistoryEntry, len(entries))
	copy(result, entries)
	sort.SliceStable(result, func(i, j int) bool {
		if result[i].Timestamp == result[j].Timestamp {
			return i > j // later index = newer when timestamps match
		}
		return result[i].Timestamp > result[j].Timestamp
	})
	return result
}

// Count returns the total number of agents.
func (s *AgentStore) Count() int {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return len(s.agents)
}

// appendHistoryLocked adds a history entry. Caller must hold s.mu (write lock).
func (s *AgentStore) appendHistoryLocked(agentID string, entry models.AgentStatusHistoryEntry) {
	s.history[agentID] = append(s.history[agentID], entry)
}
