// ABOUTME: Unit tests for Cloudlet management APIs using httptest mock server
// ABOUTME: Tests create, show, list, delete, manifest, and resource usage operations

package v2

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestCreateCloudlet(t *testing.T) {
	tests := []struct {
		name           string
		input          *NewCloudletInput
		mockStatusCode int
		mockResponse   string
		expectError    bool
	}{
		{
			name: "successful creation",
			input: &NewCloudletInput{
				Region: "us-west",
				Cloudlet: Cloudlet{
					Key: CloudletKey{
						Organization: "cloudletorg",
						Name:         "testcloudlet",
					},
					Location: Location{
						Latitude:  37.7749,
						Longitude: -122.4194,
					},
					IpSupport:     "IpSupportDynamic",
					NumDynamicIps: 10,
				},
			},
			mockStatusCode: 200,
			mockResponse:   `{"message": "success"}`,
			expectError:    false,
		},
		{
			name: "validation error",
			input: &NewCloudletInput{
				Region: "us-west",
				Cloudlet: Cloudlet{
					Key: CloudletKey{
						Organization: "",
						Name:         "testcloudlet",
					},
				},
			},
			mockStatusCode: 400,
			mockResponse:   `{"message": "organization is required"}`,
			expectError:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Create mock server
			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				assert.Equal(t, "POST", r.Method)
				assert.Equal(t, "/api/v1/auth/ctrl/CreateCloudlet", r.URL.Path)
				assert.Equal(t, "application/json", r.Header.Get("Content-Type"))

				w.WriteHeader(tt.mockStatusCode)
				_, _ = w.Write([]byte(tt.mockResponse))
			}))
			defer server.Close()

			// Create client
			client := NewClient(server.URL,
				WithHTTPClient(&http.Client{Timeout: 5 * time.Second}),
				WithAuthProvider(NewStaticTokenProvider("test-token")),
			)

			// Execute test
			ctx := context.Background()
			err := client.CreateCloudlet(ctx, tt.input)

			// Verify results
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestShowCloudlet(t *testing.T) {
	tests := []struct {
		name           string
		cloudletKey    CloudletKey
		region         string
		mockStatusCode int
		mockResponse   string
		expectError    bool
		expectNotFound bool
	}{
		{
			name: "successful show",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "testcloudlet",
			},
			region:         "us-west",
			mockStatusCode: 200,
			mockResponse: `{"data": {"key": {"organization": "cloudletorg", "name": "testcloudlet"}, "state": "Ready", "location": {"latitude": 37.7749, "longitude": -122.4194}}}
`,
			expectError:    false,
			expectNotFound: false,
		},
		{
			name: "cloudlet not found",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "nonexistent",
			},
			region:         "us-west",
			mockStatusCode: 404,
			mockResponse:   "",
			expectError:    true,
			expectNotFound: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Create mock server
			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				assert.Equal(t, "POST", r.Method)
				assert.Equal(t, "/api/v1/auth/ctrl/ShowCloudlet", r.URL.Path)

				w.WriteHeader(tt.mockStatusCode)
				if tt.mockResponse != "" {
					_, _ = w.Write([]byte(tt.mockResponse))
				}
			}))
			defer server.Close()

			// Create client
			client := NewClient(server.URL,
				WithHTTPClient(&http.Client{Timeout: 5 * time.Second}),
			)

			// Execute test
			ctx := context.Background()
			cloudlet, err := client.ShowCloudlet(ctx, tt.cloudletKey, tt.region)

			// Verify results
			if tt.expectError {
				assert.Error(t, err)
				if tt.expectNotFound {
					assert.Contains(t, err.Error(), "resource not found")
				}
			} else {
				require.NoError(t, err)
				assert.Equal(t, tt.cloudletKey.Organization, cloudlet.Key.Organization)
				assert.Equal(t, tt.cloudletKey.Name, cloudlet.Key.Name)
				assert.Equal(t, "Ready", cloudlet.State)
			}
		})
	}
}

func TestShowCloudlets(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, "POST", r.Method)
		assert.Equal(t, "/api/v1/auth/ctrl/ShowCloudlet", r.URL.Path)

		// Verify request body
		var filter CloudletFilter
		err := json.NewDecoder(r.Body).Decode(&filter)
		require.NoError(t, err)
		assert.Equal(t, "cloudletorg", filter.Cloudlet.Key.Organization)
		assert.Equal(t, "us-west", filter.Region)

		// Return multiple cloudlets
		response := `{"data": {"key": {"organization": "cloudletorg", "name": "cloudlet1"}, "state": "Ready"}}
{"data": {"key": {"organization": "cloudletorg", "name": "cloudlet2"}, "state": "Creating"}}
`
		w.WriteHeader(200)
		_, _ = w.Write([]byte(response))
	}))
	defer server.Close()

	client := NewClient(server.URL)
	ctx := context.Background()

	cloudlets, err := client.ShowCloudlets(ctx, CloudletKey{Organization: "cloudletorg"}, "us-west")

	require.NoError(t, err)
	assert.Len(t, cloudlets, 2)
	assert.Equal(t, "cloudlet1", cloudlets[0].Key.Name)
	assert.Equal(t, "Ready", cloudlets[0].State)
	assert.Equal(t, "cloudlet2", cloudlets[1].Key.Name)
	assert.Equal(t, "Creating", cloudlets[1].State)
}

func TestDeleteCloudlet(t *testing.T) {
	tests := []struct {
		name           string
		cloudletKey    CloudletKey
		region         string
		mockStatusCode int
		expectError    bool
	}{
		{
			name: "successful deletion",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "testcloudlet",
			},
			region:         "us-west",
			mockStatusCode: 200,
			expectError:    false,
		},
		{
			name: "already deleted (404 ok)",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "testcloudlet",
			},
			region:         "us-west",
			mockStatusCode: 404,
			expectError:    false,
		},
		{
			name: "server error",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "testcloudlet",
			},
			region:         "us-west",
			mockStatusCode: 500,
			expectError:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				assert.Equal(t, "POST", r.Method)
				assert.Equal(t, "/api/v1/auth/ctrl/DeleteCloudlet", r.URL.Path)

				w.WriteHeader(tt.mockStatusCode)
			}))
			defer server.Close()

			client := NewClient(server.URL)
			ctx := context.Background()

			err := client.DeleteCloudlet(ctx, tt.cloudletKey, tt.region)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestGetCloudletManifest(t *testing.T) {
	tests := []struct {
		name           string
		cloudletKey    CloudletKey
		region         string
		mockStatusCode int
		mockResponse   string
		expectError    bool
		expectNotFound bool
	}{
		{
			name: "successful manifest retrieval",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "testcloudlet",
			},
			region:         "us-west",
			mockStatusCode: 200,
			mockResponse:   `{"manifest": "apiVersion: v1\nkind: Deployment\nmetadata:\n  name: test", "last_modified": "2024-01-01T00:00:00Z"}`,
			expectError:    false,
			expectNotFound: false,
		},
		{
			name: "manifest not found",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "nonexistent",
			},
			region:         "us-west",
			mockStatusCode: 404,
			mockResponse:   "",
			expectError:    true,
			expectNotFound: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				assert.Equal(t, "POST", r.Method)
				assert.Equal(t, "/api/v1/auth/ctrl/GetCloudletManifest", r.URL.Path)

				w.WriteHeader(tt.mockStatusCode)
				if tt.mockResponse != "" {
					_, _ = w.Write([]byte(tt.mockResponse))
				}
			}))
			defer server.Close()

			client := NewClient(server.URL)
			ctx := context.Background()

			manifest, err := client.GetCloudletManifest(ctx, tt.cloudletKey, tt.region)

			if tt.expectError {
				assert.Error(t, err)
				if tt.expectNotFound {
					assert.Contains(t, err.Error(), "resource not found")
				}
			} else {
				require.NoError(t, err)
				assert.NotNil(t, manifest)
				assert.Contains(t, manifest.Manifest, "apiVersion: v1")
			}
		})
	}
}

func TestGetCloudletResourceUsage(t *testing.T) {
	tests := []struct {
		name           string
		cloudletKey    CloudletKey
		region         string
		mockStatusCode int
		mockResponse   string
		expectError    bool
		expectNotFound bool
	}{
		{
			name: "successful usage retrieval",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "testcloudlet",
			},
			region:         "us-west",
			mockStatusCode: 200,
			mockResponse:   `{"cloudlet_key": {"organization": "cloudletorg", "name": "testcloudlet"}, "region": "us-west", "usage": {"cpu": "50%", "memory": "30%", "disk": "20%"}}`,
			expectError:    false,
			expectNotFound: false,
		},
		{
			name: "usage not found",
			cloudletKey: CloudletKey{
				Organization: "cloudletorg",
				Name:         "nonexistent",
			},
			region:         "us-west",
			mockStatusCode: 404,
			mockResponse:   "",
			expectError:    true,
			expectNotFound: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				assert.Equal(t, "POST", r.Method)
				assert.Equal(t, "/api/v1/auth/ctrl/GetCloudletResourceUsage", r.URL.Path)

				w.WriteHeader(tt.mockStatusCode)
				if tt.mockResponse != "" {
					_, _ = w.Write([]byte(tt.mockResponse))
				}
			}))
			defer server.Close()

			client := NewClient(server.URL)
			ctx := context.Background()

			usage, err := client.GetCloudletResourceUsage(ctx, tt.cloudletKey, tt.region)

			if tt.expectError {
				assert.Error(t, err)
				if tt.expectNotFound {
					assert.Contains(t, err.Error(), "resource not found")
				}
			} else {
				require.NoError(t, err)
				assert.NotNil(t, usage)
				assert.Equal(t, "cloudletorg", usage.CloudletKey.Organization)
				assert.Equal(t, "testcloudlet", usage.CloudletKey.Name)
				assert.Equal(t, "us-west", usage.Region)
				assert.Contains(t, usage.Usage, "cpu")
			}
		})
	}
}
