package rdp

import "testing"

func TestNewRDPService(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })
	if svc == nil {
		t.Fatal("NewRDPService returned nil")
	}
	if len(svc.ListSessions()) != 0 {
		t.Error("new service should have no sessions")
	}
}

func TestConnectWithMockBackend(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	config := RDPConfig{
		Hostname: "test-host",
		Port:     3389,
		Username: "admin",
		Width:    1024,
		Height:   768,
	}

	sessionID, err := svc.Connect(config, 42)
	if err != nil {
		t.Fatalf("Connect error: %v", err)
	}
	if sessionID == "" {
		t.Fatal("Connect returned empty session ID")
	}

	// Verify session is tracked
	sessions := svc.ListSessions()
	if len(sessions) != 1 {
		t.Fatalf("expected 1 session, got %d", len(sessions))
	}
	if sessions[0].ID != sessionID {
		t.Errorf("session ID = %q, want %q", sessions[0].ID, sessionID)
	}
	if sessions[0].ConnID != 42 {
		t.Errorf("ConnID = %d, want 42", sessions[0].ConnID)
	}
	if sessions[0].Config.Hostname != "test-host" {
		t.Errorf("Hostname = %q, want %q", sessions[0].Config.Hostname, "test-host")
	}
}

func TestConnectDefaults(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	// Connect with zero values — should get defaults
	config := RDPConfig{Hostname: "host"}
	sessionID, err := svc.Connect(config, 1)
	if err != nil {
		t.Fatalf("Connect error: %v", err)
	}

	info, err := svc.GetSessionInfo(sessionID)
	if err != nil {
		t.Fatalf("GetSessionInfo error: %v", err)
	}

	if info.Config.Port != 3389 {
		t.Errorf("default Port = %d, want 3389", info.Config.Port)
	}
	if info.Config.Width != 1920 {
		t.Errorf("default Width = %d, want 1920", info.Config.Width)
	}
	if info.Config.Height != 1080 {
		t.Errorf("default Height = %d, want 1080", info.Config.Height)
	}
	if info.Config.ColorDepth != 32 {
		t.Errorf("default ColorDepth = %d, want 32", info.Config.ColorDepth)
	}
	if info.Config.Security != "nla" {
		t.Errorf("default Security = %q, want %q", info.Config.Security, "nla")
	}
}

func TestDisconnect(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	config := RDPConfig{Hostname: "host", Width: 800, Height: 600}
	sessionID, err := svc.Connect(config, 1)
	if err != nil {
		t.Fatalf("Connect error: %v", err)
	}

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

	if len(svc.ListSessions()) != 0 {
		t.Error("expected 0 sessions after disconnect")
	}

	// Verify double-disconnect returns error
	if err := svc.Disconnect(sessionID); err == nil {
		t.Error("expected error on double disconnect")
	}
}

func TestDisconnectNotFound(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })
	if err := svc.Disconnect("nonexistent"); err == nil {
		t.Error("expected error for nonexistent session")
	}
}

func TestSessionTracking(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	config := RDPConfig{Hostname: "host", Width: 640, Height: 480}

	id1, _ := svc.Connect(config, 1)
	id2, _ := svc.Connect(config, 2)
	id3, _ := svc.Connect(config, 3)

	if len(svc.ListSessions()) != 3 {
		t.Fatalf("expected 3 sessions, got %d", len(svc.ListSessions()))
	}

	// Disconnect middle session
	if err := svc.Disconnect(id2); err != nil {
		t.Fatalf("Disconnect error: %v", err)
	}

	sessions := svc.ListSessions()
	if len(sessions) != 2 {
		t.Fatalf("expected 2 sessions, got %d", len(sessions))
	}

	// Verify remaining sessions
	ids := make(map[string]bool)
	for _, s := range sessions {
		ids[s.ID] = true
	}
	if !ids[id1] || !ids[id3] {
		t.Error("remaining sessions should be id1 and id3")
	}
}

func TestSendMouse(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	config := RDPConfig{Hostname: "host", Width: 800, Height: 600}
	sessionID, _ := svc.Connect(config, 1)

	if err := svc.SendMouse(sessionID, 100, 200, MouseFlagMove); err != nil {
		t.Errorf("SendMouse error: %v", err)
	}

	if err := svc.SendMouse(sessionID, 100, 200, MouseFlagButton1|MouseFlagDown); err != nil {
		t.Errorf("SendMouse click error: %v", err)
	}
}

func TestSendMouseNotFound(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })
	if err := svc.SendMouse("nonexistent", 0, 0, 0); err == nil {
		t.Error("expected error for nonexistent session")
	}
}

func TestSendKey(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	config := RDPConfig{Hostname: "host", Width: 800, Height: 600}
	sessionID, _ := svc.Connect(config, 1)

	// Send key down
	sc, _ := JSKeyToScancode("KeyA")
	if err := svc.SendKey(sessionID, sc, true); err != nil {
		t.Errorf("SendKey down error: %v", err)
	}

	// Send key up
	if err := svc.SendKey(sessionID, sc, false); err != nil {
		t.Errorf("SendKey up error: %v", err)
	}
}

func TestSendKeyNotFound(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })
	if err := svc.SendKey("nonexistent", 0x001E, true); err == nil {
		t.Error("expected error for nonexistent session")
	}
}

func TestSendClipboard(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	config := RDPConfig{Hostname: "host", Width: 800, Height: 600}
	sessionID, _ := svc.Connect(config, 1)

	if err := svc.SendClipboard(sessionID, "hello clipboard"); err != nil {
		t.Errorf("SendClipboard error: %v", err)
	}
}

func TestGetFrame(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	config := RDPConfig{Hostname: "host", Width: 100, Height: 100}
	sessionID, _ := svc.Connect(config, 1)

	frame, err := svc.GetFrame(sessionID)
	if err != nil {
		t.Fatalf("GetFrame error: %v", err)
	}

	expectedLen := 100 * 100 * 4
	if len(frame) != expectedLen {
		t.Errorf("frame length = %d, want %d", len(frame), expectedLen)
	}

	// Verify the frame is not all zeros (mock generates colored content)
	allZero := true
	for _, b := range frame {
		if b != 0 {
			allZero = false
			break
		}
	}
	if allZero {
		t.Error("mock frame should not be all zeros")
	}
}

func TestGetFrameNotFound(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })
	_, err := svc.GetFrame("nonexistent")
	if err == nil {
		t.Error("expected error for nonexistent session")
	}
}

func TestIsConnected(t *testing.T) {
	svc := NewRDPService(func() RDPBackend { return NewMockBackend() })

	config := RDPConfig{Hostname: "host", Width: 800, Height: 600}
	sessionID, _ := svc.Connect(config, 1)

	if !svc.IsConnected(sessionID) {
		t.Error("session should be connected")
	}

	svc.Disconnect(sessionID)

	if svc.IsConnected(sessionID) {
		t.Error("session should not be connected after disconnect")
	}
}
