package daemon

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// RegistryEntry represents a daemon entry in the registry
type RegistryEntry struct {
	WorkspacePath string    `json:"workspace_path"`
	SocketPath    string    `json:"socket_path"`
	DatabasePath  string    `json:"database_path"`
	PID           int       `json:"pid"`
	Version       string    `json:"version"`
	StartedAt     time.Time `json:"started_at"`
}

// Registry manages the global daemon registry file
type Registry struct {
	path string
	mu   sync.Mutex
}

// NewRegistry creates a new registry instance
// The registry is stored in ~/.beads/registry.json
func NewRegistry() (*Registry, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return nil, fmt.Errorf("failed to get home directory: %w", err)
	}

	beadsDir := filepath.Join(home, ".beads")
	if err := os.MkdirAll(beadsDir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create .beads directory: %w", err)
	}

	registryPath := filepath.Join(beadsDir, "registry.json")
	return &Registry{path: registryPath}, nil
}

// readEntries reads all entries from the registry file
func (r *Registry) readEntries() ([]RegistryEntry, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	data, err := os.ReadFile(r.path)
	if err != nil {
		if os.IsNotExist(err) {
			return []RegistryEntry{}, nil
		}
		return nil, fmt.Errorf("failed to read registry: %w", err)
	}

	var entries []RegistryEntry
	if err := json.Unmarshal(data, &entries); err != nil {
		return nil, fmt.Errorf("failed to parse registry: %w", err)
	}

	return entries, nil
}

// writeEntries writes all entries to the registry file
func (r *Registry) writeEntries(entries []RegistryEntry) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	// Ensure we always write an array, never null
	if entries == nil {
		entries = []RegistryEntry{}
	}

	data, err := json.MarshalIndent(entries, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal registry: %w", err)
	}

	if err := os.WriteFile(r.path, data, 0644); err != nil {
		return fmt.Errorf("failed to write registry: %w", err)
	}

	return nil
}

// Register adds a daemon to the registry
func (r *Registry) Register(entry RegistryEntry) error {
	entries, err := r.readEntries()
	if err != nil {
		return err
	}

	// Remove any existing entry for this workspace or PID
	filtered := []RegistryEntry{}
	for _, e := range entries {
		if e.WorkspacePath != entry.WorkspacePath && e.PID != entry.PID {
			filtered = append(filtered, e)
		}
	}

	// Add new entry
	filtered = append(filtered, entry)

	return r.writeEntries(filtered)
}

// Unregister removes a daemon from the registry
func (r *Registry) Unregister(workspacePath string, pid int) error {
	entries, err := r.readEntries()
	if err != nil {
		return err
	}

	// Filter out entries matching workspace or PID
	filtered := []RegistryEntry{}
	for _, e := range entries {
		if e.WorkspacePath != workspacePath && e.PID != pid {
			filtered = append(filtered, e)
		}
	}

	return r.writeEntries(filtered)
}

// List returns all daemons from the registry, automatically cleaning up stale entries
func (r *Registry) List() ([]DaemonInfo, error) {
	entries, err := r.readEntries()
	if err != nil {
		return nil, err
	}

	var daemons []DaemonInfo
	var aliveEntries []RegistryEntry

	for _, entry := range entries {
		// Check if process is still alive
		if !isProcessAlive(entry.PID) {
			// Stale entry - skip and don't add to alive list
			continue
		}

		// Process is alive, add to both lists
		aliveEntries = append(aliveEntries, entry)

		// Try to connect and get current status
		daemon := discoverDaemon(entry.SocketPath)
		
		// If connection failed but process is alive, still include basic info
		if !daemon.Alive {
			daemon.Alive = true // Process exists, socket just might not be ready
			daemon.WorkspacePath = entry.WorkspacePath
			daemon.DatabasePath = entry.DatabasePath
			daemon.SocketPath = entry.SocketPath
			daemon.PID = entry.PID
			daemon.Version = entry.Version
		}

		daemons = append(daemons, daemon)
	}

	// Clean up stale entries from registry
	if len(aliveEntries) != len(entries) {
		if err := r.writeEntries(aliveEntries); err != nil {
			// Log warning but don't fail - we still have the daemon list
			fmt.Fprintf(os.Stderr, "Warning: failed to cleanup stale registry entries: %v\n", err)
		}
	}

	return daemons, nil
}

// Clear removes all entries from the registry (for testing)
func (r *Registry) Clear() error {
	return r.writeEntries([]RegistryEntry{})
}
