package rpc

import (
	"context"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/steveyegge/beads/internal/storage/sqlite"
)

func TestStatusEndpoint(t *testing.T) {
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")
	socketPath := newTestSocketPath(t)

	store, err := sqlite.New(context.Background(), dbPath)
	if err != nil {
		t.Fatalf("failed to create storage: %v", err)
	}
	defer store.Close()

	server := NewServer(socketPath, store, tmpDir, dbPath)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		_ = server.Start(ctx)
	}()

	<-server.WaitReady()
	defer server.Stop()

	client, err := TryConnect(socketPath)
	if err != nil {
		t.Fatalf("failed to connect: %v", err)
	}
	if client == nil {
		t.Fatal("client is nil")
	}
	defer client.Close()

	// Test status endpoint
	status, err := client.Status()
	if err != nil {
		t.Fatalf("status call failed: %v", err)
	}

	// Verify response fields
	if status.Version == "" {
		t.Error("expected version to be set")
	}
	if status.WorkspacePath != tmpDir {
		t.Errorf("expected workspace path %s, got %s", tmpDir, status.WorkspacePath)
	}
	if status.DatabasePath != dbPath {
		t.Errorf("expected database path %s, got %s", dbPath, status.DatabasePath)
	}
	if status.SocketPath != socketPath {
		t.Errorf("expected socket path %s, got %s", socketPath, status.SocketPath)
	}
	if status.PID != os.Getpid() {
		t.Errorf("expected PID %d, got %d", os.Getpid(), status.PID)
	}
	if status.UptimeSeconds <= 0 {
		t.Error("expected positive uptime")
	}
	if status.LastActivityTime == "" {
		t.Error("expected last activity time to be set")
	}
	if status.ExclusiveLockActive {
		t.Error("expected no exclusive lock in test")
	}

	// Verify last activity time is recent
	lastActivity, err := time.Parse(time.RFC3339, status.LastActivityTime)
	if err != nil {
		t.Errorf("failed to parse last activity time: %v", err)
	}
	if time.Since(lastActivity) > 5*time.Second {
		t.Errorf("last activity time too old: %v", lastActivity)
	}
}

func TestStatusEndpointWithConfig(t *testing.T) {
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")
	socketPath := newTestSocketPath(t)
	store, err := sqlite.New(context.Background(), dbPath)
	if err != nil {
		t.Fatalf("failed to create storage: %v", err)
	}
	defer store.Close()

	server := NewServer(socketPath, store, tmpDir, dbPath)

	// Set config before starting
	server.SetConfig(true, true, true, false, "10s", "events")

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		_ = server.Start(ctx)
	}()

	<-server.WaitReady()
	defer server.Stop()

	client, err := TryConnect(socketPath)
	if err != nil {
		t.Fatalf("failed to connect: %v", err)
	}
	if client == nil {
		t.Fatal("client is nil")
	}
	defer client.Close()

	// Test status endpoint
	status, err := client.Status()
	if err != nil {
		t.Fatalf("status call failed: %v", err)
	}

	// Verify config fields
	if !status.AutoCommit {
		t.Error("expected AutoCommit to be true")
	}
	if !status.AutoPush {
		t.Error("expected AutoPush to be true")
	}
	if status.LocalMode {
		t.Error("expected LocalMode to be false")
	}
	if status.SyncInterval != "10s" {
		t.Errorf("expected SyncInterval '10s', got '%s'", status.SyncInterval)
	}
	if status.DaemonMode != "events" {
		t.Errorf("expected DaemonMode 'events', got '%s'", status.DaemonMode)
	}
}

func TestStatusEndpointLocalMode(t *testing.T) {
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")
	socketPath := newTestSocketPath(t)
	store, err := sqlite.New(context.Background(), dbPath)
	if err != nil {
		t.Fatalf("failed to create storage: %v", err)
	}
	defer store.Close()

	server := NewServer(socketPath, store, tmpDir, dbPath)

	// Set config for local mode
	server.SetConfig(false, false, false, true, "5s", "poll")

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		_ = server.Start(ctx)
	}()

	<-server.WaitReady()
	defer server.Stop()

	client, err := TryConnect(socketPath)
	if err != nil {
		t.Fatalf("failed to connect: %v", err)
	}
	if client == nil {
		t.Fatal("client is nil")
	}
	defer client.Close()

	// Test status endpoint
	status, err := client.Status()
	if err != nil {
		t.Fatalf("status call failed: %v", err)
	}

	// Verify local mode config
	if status.AutoCommit {
		t.Error("expected AutoCommit to be false in local mode")
	}
	if status.AutoPush {
		t.Error("expected AutoPush to be false in local mode")
	}
	if !status.LocalMode {
		t.Error("expected LocalMode to be true")
	}
	if status.SyncInterval != "5s" {
		t.Errorf("expected SyncInterval '5s', got '%s'", status.SyncInterval)
	}
	if status.DaemonMode != "poll" {
		t.Errorf("expected DaemonMode 'poll', got '%s'", status.DaemonMode)
	}
}

func TestStatusEndpointDefaultConfig(t *testing.T) {
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")
	socketPath := newTestSocketPath(t)

	store, err := sqlite.New(context.Background(), dbPath)
	if err != nil {
		t.Fatalf("failed to create storage: %v", err)
	}
	defer store.Close()

	server := NewServer(socketPath, store, tmpDir, dbPath)
	// Don't call SetConfig - test default values

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		_ = server.Start(ctx)
	}()

	<-server.WaitReady()
	defer server.Stop()

	client, err := TryConnect(socketPath)
	if err != nil {
		t.Fatalf("failed to connect: %v", err)
	}
	if client == nil {
		t.Fatal("client is nil")
	}
	defer client.Close()

	// Test status endpoint
	status, err := client.Status()
	if err != nil {
		t.Fatalf("status call failed: %v", err)
	}

	// Verify default config (all false/empty when SetConfig not called)
	if status.AutoCommit {
		t.Error("expected AutoCommit to be false by default")
	}
	if status.AutoPush {
		t.Error("expected AutoPush to be false by default")
	}
	if status.LocalMode {
		t.Error("expected LocalMode to be false by default")
	}
	if status.SyncInterval != "" {
		t.Errorf("expected SyncInterval to be empty by default, got '%s'", status.SyncInterval)
	}
	if status.DaemonMode != "" {
		t.Errorf("expected DaemonMode to be empty by default, got '%s'", status.DaemonMode)
	}
}

func TestSetConfigConcurrency(t *testing.T) {
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")
	socketPath := newTestSocketPath(t)

	store, err := sqlite.New(context.Background(), dbPath)
	if err != nil {
		t.Fatalf("failed to create storage: %v", err)
	}
	defer store.Close()

	server := NewServer(socketPath, store, tmpDir, dbPath)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		_ = server.Start(ctx)
	}()

	<-server.WaitReady()
	defer server.Stop()

	// Test concurrent SetConfig calls don't race
	done := make(chan bool)
	for i := 0; i < 10; i++ {
		go func(n int) {
			server.SetConfig(n%2 == 0, n%3 == 0, n%5 == 0, n%4 == 0, "5s", "events")
			done <- true
		}(i)
	}

	// Wait for all goroutines
	for i := 0; i < 10; i++ {
		<-done
	}

	// Verify we can still get status (server didn't crash)
	client, err := TryConnect(socketPath)
	if err != nil {
		t.Fatalf("failed to connect: %v", err)
	}
	defer client.Close()

	status, err := client.Status()
	if err != nil {
		t.Fatalf("status call failed after concurrent SetConfig: %v", err)
	}

	// Just verify the status call succeeded - values will be from last SetConfig
	t.Logf("Final config: AutoCommit=%v, AutoPush=%v, LocalMode=%v",
		status.AutoCommit, status.AutoPush, status.LocalMode)
}
