package connections

import (
	"path/filepath"
	"testing"

	"github.com/vstockwell/wraith/internal/db"
)

func strPtr(s string) *string { return &s }

func setupTestService(t *testing.T) *ConnectionService {
	t.Helper()
	dir := t.TempDir()
	database, err := db.Open(filepath.Join(dir, "test.db"))
	if err != nil {
		t.Fatalf("db.Open() error: %v", err)
	}
	if err := db.Migrate(database); err != nil {
		t.Fatalf("db.Migrate() error: %v", err)
	}
	t.Cleanup(func() { database.Close() })
	return NewConnectionService(database)
}

func TestCreateGroup(t *testing.T) {
	svc := setupTestService(t)

	g, err := svc.CreateGroup("Servers", nil)
	if err != nil {
		t.Fatalf("CreateGroup() error: %v", err)
	}
	if g.ID == 0 {
		t.Error("expected non-zero ID")
	}
	if g.Name != "Servers" {
		t.Errorf("Name = %q, want %q", g.Name, "Servers")
	}
	if g.ParentID != nil {
		t.Errorf("ParentID = %v, want nil", g.ParentID)
	}
}

func TestCreateSubGroup(t *testing.T) {
	svc := setupTestService(t)

	parent, err := svc.CreateGroup("Servers", nil)
	if err != nil {
		t.Fatalf("CreateGroup(parent) error: %v", err)
	}

	child, err := svc.CreateGroup("Production", &parent.ID)
	if err != nil {
		t.Fatalf("CreateGroup(child) error: %v", err)
	}
	if child.ParentID == nil {
		t.Fatal("expected non-nil ParentID")
	}
	if *child.ParentID != parent.ID {
		t.Errorf("ParentID = %d, want %d", *child.ParentID, parent.ID)
	}
}

func TestListGroups(t *testing.T) {
	svc := setupTestService(t)

	parent, err := svc.CreateGroup("Servers", nil)
	if err != nil {
		t.Fatalf("CreateGroup(parent) error: %v", err)
	}
	if _, err := svc.CreateGroup("Production", &parent.ID); err != nil {
		t.Fatalf("CreateGroup(child) error: %v", err)
	}

	groups, err := svc.ListGroups()
	if err != nil {
		t.Fatalf("ListGroups() error: %v", err)
	}
	if len(groups) != 1 {
		t.Fatalf("len(groups) = %d, want 1 (only root groups)", len(groups))
	}
	if groups[0].Name != "Servers" {
		t.Errorf("groups[0].Name = %q, want %q", groups[0].Name, "Servers")
	}
	if len(groups[0].Children) != 1 {
		t.Fatalf("len(children) = %d, want 1", len(groups[0].Children))
	}
	if groups[0].Children[0].Name != "Production" {
		t.Errorf("child name = %q, want %q", groups[0].Children[0].Name, "Production")
	}
}

func TestDeleteGroup(t *testing.T) {
	svc := setupTestService(t)

	g, err := svc.CreateGroup("ToDelete", nil)
	if err != nil {
		t.Fatalf("CreateGroup() error: %v", err)
	}

	if err := svc.DeleteGroup(g.ID); err != nil {
		t.Fatalf("DeleteGroup() error: %v", err)
	}

	groups, err := svc.ListGroups()
	if err != nil {
		t.Fatalf("ListGroups() error: %v", err)
	}
	if len(groups) != 0 {
		t.Errorf("len(groups) = %d, want 0 after delete", len(groups))
	}
}

func TestCreateConnection(t *testing.T) {
	svc := setupTestService(t)

	conn, err := svc.CreateConnection(CreateConnectionInput{
		Name:     "Web Server",
		Hostname: "10.0.0.1",
		Port:     22,
		Protocol: "ssh",
		Tags:     []string{"Prod", "Linux"},
		Options:  `{"keepAliveInterval": 60}`,
	})
	if err != nil {
		t.Fatalf("CreateConnection() error: %v", err)
	}
	if conn.ID == 0 {
		t.Error("expected non-zero ID")
	}
	if conn.Name != "Web Server" {
		t.Errorf("Name = %q, want %q", conn.Name, "Web Server")
	}
	if len(conn.Tags) != 2 {
		t.Fatalf("len(Tags) = %d, want 2", len(conn.Tags))
	}
	if conn.Tags[0] != "Prod" || conn.Tags[1] != "Linux" {
		t.Errorf("Tags = %v, want [Prod Linux]", conn.Tags)
	}
	if conn.Options != `{"keepAliveInterval": 60}` {
		t.Errorf("Options = %q, want JSON blob", conn.Options)
	}
}

func TestListConnections(t *testing.T) {
	svc := setupTestService(t)

	if _, err := svc.CreateConnection(CreateConnectionInput{
		Name:     "Server A",
		Hostname: "10.0.0.1",
		Port:     22,
		Protocol: "ssh",
	}); err != nil {
		t.Fatalf("CreateConnection(A) error: %v", err)
	}
	if _, err := svc.CreateConnection(CreateConnectionInput{
		Name:     "Server B",
		Hostname: "10.0.0.2",
		Port:     3389,
		Protocol: "rdp",
	}); err != nil {
		t.Fatalf("CreateConnection(B) error: %v", err)
	}

	conns, err := svc.ListConnections()
	if err != nil {
		t.Fatalf("ListConnections() error: %v", err)
	}
	if len(conns) != 2 {
		t.Fatalf("len(conns) = %d, want 2", len(conns))
	}
}

func TestUpdateConnection(t *testing.T) {
	svc := setupTestService(t)

	conn, err := svc.CreateConnection(CreateConnectionInput{
		Name:     "Old Name",
		Hostname: "10.0.0.1",
		Port:     22,
		Protocol: "ssh",
		Tags:     []string{"Dev"},
	})
	if err != nil {
		t.Fatalf("CreateConnection() error: %v", err)
	}

	updated, err := svc.UpdateConnection(conn.ID, UpdateConnectionInput{
		Name: strPtr("New Name"),
		Tags: []string{"Prod", "Linux"},
	})
	if err != nil {
		t.Fatalf("UpdateConnection() error: %v", err)
	}
	if updated.Name != "New Name" {
		t.Errorf("Name = %q, want %q", updated.Name, "New Name")
	}
	if len(updated.Tags) != 2 {
		t.Fatalf("len(Tags) = %d, want 2", len(updated.Tags))
	}
	if updated.Tags[0] != "Prod" {
		t.Errorf("Tags[0] = %q, want %q", updated.Tags[0], "Prod")
	}
	// Hostname should remain unchanged
	if updated.Hostname != "10.0.0.1" {
		t.Errorf("Hostname = %q, want %q (unchanged)", updated.Hostname, "10.0.0.1")
	}
}

func TestDeleteConnection(t *testing.T) {
	svc := setupTestService(t)

	conn, err := svc.CreateConnection(CreateConnectionInput{
		Name:     "ToDelete",
		Hostname: "10.0.0.1",
		Port:     22,
		Protocol: "ssh",
	})
	if err != nil {
		t.Fatalf("CreateConnection() error: %v", err)
	}

	if err := svc.DeleteConnection(conn.ID); err != nil {
		t.Fatalf("DeleteConnection() error: %v", err)
	}

	conns, err := svc.ListConnections()
	if err != nil {
		t.Fatalf("ListConnections() error: %v", err)
	}
	if len(conns) != 0 {
		t.Errorf("len(conns) = %d, want 0 after delete", len(conns))
	}
}
