package api_test

import (
	"bytes"
	"encoding/binary"
	"testing"
)

// Test payload building and validation
func TestBuildStartJupyterPayload(t *testing.T) {
	tests := []struct {
		name       string
		apiKeyHash []byte
		svcName    string
		workspace  string
		password   string
		wantLen    int
	}{
		{
			name:       "valid payload",
			apiKeyHash: make([]byte, 16),
			svcName:    "test-service",
			workspace:  "/tmp/workspace",
			password:   "mypass",
			wantLen:    16 + 1 + 12 + 2 + 14 + 1 + 6,
		},
		{
			name:       "empty password",
			apiKeyHash: make([]byte, 16),
			svcName:    "test",
			workspace:  "/tmp",
			password:   "",
			wantLen:    16 + 1 + 4 + 2 + 4 + 1 + 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			payload := buildStartJupyterPayload(t, &startJupyterParams{
				apiKeyHash: tt.apiKeyHash,
				name:       tt.svcName,
				workspace:  tt.workspace,
				password:   tt.password,
			})

			if len(payload) != tt.wantLen {
				t.Errorf("expected payload length %d, got %d", tt.wantLen, len(payload))
			}

			// Verify API key hash
			if !bytes.Equal(payload[0:16], tt.apiKeyHash) {
				t.Error("API key hash mismatch")
			}

			// Verify name length and content
			nameLen := int(payload[16])
			if nameLen != len(tt.svcName) {
				t.Errorf("expected name length %d, got %d", len(tt.svcName), nameLen)
			}
		})
	}
}

func TestBuildStopJupyterPayload(t *testing.T) {
	apiKeyHash := make([]byte, 16)
	serviceID := "test-service-123"

	payload := buildStopJupyterPayload(t, apiKeyHash, serviceID)

	// Verify length
	expectedLen := 16 + 1 + len(serviceID)
	if len(payload) != expectedLen {
		t.Errorf("expected payload length %d, got %d", expectedLen, len(payload))
	}

	// Verify API key hash
	if !bytes.Equal(payload[0:16], apiKeyHash) {
		t.Error("API key hash mismatch")
	}

	// Verify service ID length
	idLen := int(payload[16])
	if idLen != len(serviceID) {
		t.Errorf("expected service ID length %d, got %d", len(serviceID), idLen)
	}

	// Verify service ID content
	actualID := string(payload[17:])
	if actualID != serviceID {
		t.Errorf("expected service ID %s, got %s", serviceID, actualID)
	}
}

func TestBuildListJupyterPayload(t *testing.T) {
	apiKeyHash := make([]byte, 16)
	for i := range apiKeyHash {
		apiKeyHash[i] = byte(i)
	}

	payload := buildListJupyterPayload(t, apiKeyHash)

	// List payload should only be API key hash
	if len(payload) != 16 {
		t.Errorf("expected payload length 16, got %d", len(payload))
	}

	if !bytes.Equal(payload, apiKeyHash) {
		t.Error("API key hash mismatch in list payload")
	}
}

func TestJupyterPayloadValidation(t *testing.T) {
	tests := []struct {
		name      string
		payload   []byte
		shouldErr bool
	}{
		{
			name:      "start payload too short",
			payload:   make([]byte, 10),
			shouldErr: true,
		},
		{
			name: "valid minimum start payload",
			payload: buildStartJupyterPayload(t, &startJupyterParams{
				apiKeyHash: make([]byte, 16),
				name:       "a",
				workspace:  "/",
				password:   "",
			}),
			shouldErr: false,
		},
		{
			name:      "stop payload too short",
			payload:   make([]byte, 5),
			shouldErr: true,
		},
		{
			name:      "valid stop payload",
			payload:   buildStopJupyterPayload(t, make([]byte, 16), "test-id"),
			shouldErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Just validate payload structure
			if tt.shouldErr {
				// Check basic length requirements
				if len(tt.payload) >= 16 {
					t.Error("expected short payload but got sufficient length")
				}
			} else {
				// Check minimum length satisfied
				if len(tt.payload) < 16 {
					t.Error("payload too short for API key hash")
				}
			}
		})
	}
}

// Test that payload parsing logic is correct
func TestJupyterPayloadParsing(t *testing.T) {
	t.Run("parse start jupyter payload", func(t *testing.T) {
		params := &startJupyterParams{
			apiKeyHash: make([]byte, 16),
			name:       "test-notebook",
			workspace:  "/home/user/notebooks",
			password:   "secret123",
		}

		payload := buildStartJupyterPayload(t, params)

		// Parse it back
		offset := 0

		// API key hash
		apiKeyHash := payload[offset : offset+16]
		offset += 16
		if !bytes.Equal(apiKeyHash, params.apiKeyHash) {
			t.Error("API key hash mismatch after parsing")
		}

		// Name
		nameLen := int(payload[offset])
		offset++
		name := string(payload[offset : offset+nameLen])
		offset += nameLen
		if name != params.name {
			t.Errorf("expected name %s, got %s", params.name, name)
		}

		// Workspace
		workspaceLen := int(binary.BigEndian.Uint16(payload[offset : offset+2]))
		offset += 2
		workspace := string(payload[offset : offset+workspaceLen])
		offset += workspaceLen
		if workspace != params.workspace {
			t.Errorf("expected workspace %s, got %s", params.workspace, workspace)
		}

		// Password
		passwordLen := int(payload[offset])
		offset++
		password := string(payload[offset : offset+passwordLen])
		if password != params.password {
			t.Errorf("expected password %s, got %s", params.password, password)
		}
	})
}

// Helper functions to build test payloads

type startJupyterParams struct {
	apiKeyHash []byte
	name       string
	workspace  string
	password   string
}

func buildStartJupyterPayload(t *testing.T, params *startJupyterParams) []byte {
	t.Helper()

	buf := new(bytes.Buffer)

	// API key hash (16 bytes)
	buf.Write(params.apiKeyHash)

	// Name length + name
	buf.WriteByte(byte(len(params.name)))
	buf.WriteString(params.name)

	// Workspace length (2 bytes) + workspace
	binary.Write(buf, binary.BigEndian, uint16(len(params.workspace)))
	buf.WriteString(params.workspace)

	// Password length + password
	buf.WriteByte(byte(len(params.password)))
	buf.WriteString(params.password)

	return buf.Bytes()
}

func buildStopJupyterPayload(t *testing.T, apiKeyHash []byte, serviceID string) []byte {
	t.Helper()

	buf := new(bytes.Buffer)

	// API key hash (16 bytes)
	buf.Write(apiKeyHash)

	// Service ID length + service ID
	buf.WriteByte(byte(len(serviceID)))
	buf.WriteString(serviceID)

	return buf.Bytes()
}

func buildListJupyterPayload(t *testing.T, apiKeyHash []byte) []byte {
	t.Helper()

	// Only API key hash needed
	return apiKeyHash
}
