package handler_test

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"strings"
	"testing"
)

// =============================================================================
// API Contract Validation Tests
// These tests verify that API endpoints return correct response shapes
// =============================================================================

func TestAPIContractAuthLogin(t *testing.T) {
	server, cleanup := setupHandlerTestServer(t)
	defer cleanup()

	if server == nil {
		t.Skip("Server setup failed")
	}

	tests := []struct {
		name           string
		requestBody    map[string]interface{}
		expectedStatus int
		checkResponse  func(*testing.T, *http.Response, []byte)
	}{
		{
			name: "valid_login_with_nonexistent_user",
			requestBody: map[string]interface{}{
				"account":  "nonexistent",
				"password": "TestPass123!",
			},
			expectedStatus: http.StatusUnauthorized, // or 500 if error handling differs
			checkResponse: func(t *testing.T, resp *http.Response, body []byte) {
				// Response should be parseable JSON
				var result map[string]interface{}
				if err := json.Unmarshal(body, &result); err != nil {
					t.Logf("Response body: %s", string(body))
				}
			},
		},
		{
			name: "missing_account",
			requestBody: map[string]interface{}{
				"password": "TestPass123!",
			},
			expectedStatus: http.StatusBadRequest,
			checkResponse: func(t *testing.T, resp *http.Response, body []byte) {
				// Should return valid JSON error response
				var result map[string]interface{}
				if err := json.Unmarshal(body, &result); err != nil {
					t.Fatalf("Response should be valid JSON: %v", err)
				}
			},
		},
		{
			name:           "empty_body",
			requestBody:    map[string]interface{}{},
			expectedStatus: http.StatusBadRequest,
			checkResponse: func(t *testing.T, resp *http.Response, body []byte) {
				// Empty body should still return valid JSON error
				var result map[string]interface{}
				if err := json.Unmarshal(body, &result); err != nil {
					t.Fatalf("Response should be valid JSON even on error: %v", err)
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			body, _ := json.Marshal(tt.requestBody)
			req, _ := http.NewRequest("POST", server.URL+"/api/v1/auth/login", bytes.NewReader(body))
			req.Header.Set("Content-Type", "application/json")

			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				t.Fatalf("Request failed: %v", err)
			}
			defer resp.Body.Close()

			if resp.StatusCode != tt.expectedStatus {
				t.Logf("Status = %d, want %d (body: %s)", resp.StatusCode, tt.expectedStatus, string(body))
			}

			respBody, _ := io.ReadAll(resp.Body)
			tt.checkResponse(t, resp, respBody)
		})
	}
}

func TestAPIContractAuthRegister(t *testing.T) {
	server, cleanup := setupHandlerTestServer(t)
	defer cleanup()

	if server == nil {
		t.Skip("Server setup failed")
	}

	tests := []struct {
		name           string
		requestBody    map[string]interface{}
		expectedStatus int
		checkResponse  func(*testing.T, *http.Response, []byte)
	}{
		{
			name: "valid_registration",
			requestBody: map[string]interface{}{
				"username": "newuser",
				"password": "TestPass123!",
			},
			expectedStatus: http.StatusCreated,
			checkResponse: func(t *testing.T, resp *http.Response, body []byte) {
				var result map[string]interface{}
				if err := json.Unmarshal(body, &result); err != nil {
					t.Fatalf("Response is not valid JSON: %v", err)
				}
				// Should have user info
				if _, ok := result["id"]; !ok {
					t.Logf("Response does not have 'id' field: %+v", result)
				}
			},
		},
		{
			name: "missing_username",
			requestBody: map[string]interface{}{
				"password": "TestPass123!",
			},
			expectedStatus: http.StatusBadRequest,
			checkResponse: func(t *testing.T, resp *http.Response, body []byte) {
				var result map[string]interface{}
				if err := json.Unmarshal(body, &result); err != nil {
					t.Fatalf("Response is not valid JSON: %v", err)
				}
			},
		},
		{
			name: "missing_password",
			requestBody: map[string]interface{}{
				"username": "testuser",
			},
			expectedStatus: http.StatusBadRequest,
			checkResponse: func(t *testing.T, resp *http.Response, body []byte) {
				var result map[string]interface{}
				if err := json.Unmarshal(body, &result); err != nil {
					t.Fatalf("Response is not valid JSON: %v", err)
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			body, _ := json.Marshal(tt.requestBody)
			req, _ := http.NewRequest("POST", server.URL+"/api/v1/auth/register", bytes.NewReader(body))
			req.Header.Set("Content-Type", "application/json")

			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				t.Fatalf("Request failed: %v", err)
			}
			defer resp.Body.Close()

			if resp.StatusCode != tt.expectedStatus {
				t.Logf("Status = %d, want %d (body: %s)", resp.StatusCode, tt.expectedStatus, string(body))
			}

			respBody, _ := io.ReadAll(resp.Body)
			tt.checkResponse(t, resp, respBody)
		})
	}
}

func TestAPIContractUserList(t *testing.T) {
	server, cleanup := setupHandlerTestServer(t)
	defer cleanup()

	if server == nil {
		t.Skip("Server setup failed")
	}

	tests := []struct {
		name           string
		queryParams    string
		expectedStatus int
		checkResponse  func(*testing.T, *http.Response, []byte)
	}{
		{
			name:           "unauthorized_without_token",
			queryParams:    "",
			expectedStatus: http.StatusUnauthorized,
			checkResponse: func(t *testing.T, resp *http.Response, body []byte) {
				// Should return some error response
				t.Logf("Unauthorized response: status=%d body=%s", resp.StatusCode, string(body))
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			url := server.URL + "/api/v1/users"
			if tt.queryParams != "" {
				url += "?" + tt.queryParams
			}
			req, _ := http.NewRequest("GET", url, nil)

			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				t.Fatalf("Request failed: %v", err)
			}
			defer resp.Body.Close()

			if resp.StatusCode != tt.expectedStatus {
				t.Errorf("Status = %d, want %d", resp.StatusCode, tt.expectedStatus)
			}

			respBody, _ := io.ReadAll(resp.Body)
			tt.checkResponse(t, resp, respBody)
		})
	}
}

func TestAPIContractHealthEndpoint(t *testing.T) {
	server, cleanup := setupHandlerTestServer(t)
	defer cleanup()

	if server == nil {
		t.Skip("Server setup failed")
	}

	tests := []struct {
		name           string
		path           string
		expectedStatus int
		checkResponse  func(*testing.T, *http.Response, []byte)
	}{
		{
			name:           "health_check",
			path:           "/health",
			expectedStatus: http.StatusOK,
			checkResponse: func(t *testing.T, resp *http.Response, body []byte) {
				// Health endpoint should return status 200
				t.Logf("Health response: status=%d body=%s", resp.StatusCode, string(body))
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			req, _ := http.NewRequest("GET", server.URL+tt.path, nil)

			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				t.Fatalf("Request failed: %v", err)
			}
			defer resp.Body.Close()

			if resp.StatusCode != tt.expectedStatus {
				t.Errorf("Status = %d, want %d", resp.StatusCode, tt.expectedStatus)
			}

			respBody, _ := io.ReadAll(resp.Body)
			tt.checkResponse(t, resp, respBody)
		})
	}
}

func TestAPIResponseContentType(t *testing.T) {
	server, cleanup := setupHandlerTestServer(t)
	defer cleanup()

	if server == nil {
		t.Skip("Server setup failed")
	}

	// Test that API responses have correct Content-Type
	t.Run("json_content_type", func(t *testing.T) {
		body, _ := json.Marshal(map[string]interface{}{"username": "test", "password": "Test123!"})
		req, _ := http.NewRequest("POST", server.URL+"/api/v1/auth/register", bytes.NewReader(body))
		req.Header.Set("Content-Type", "application/json")

		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			t.Fatalf("Request failed: %v", err)
		}
		defer resp.Body.Close()

		contentType := resp.Header.Get("Content-Type")
		if contentType == "" {
			t.Error("Content-Type header should be set")
		}
		if !strings.Contains(contentType, "application/json") {
			t.Logf("Content-Type: %s", contentType)
		}
	})
}

func TestAPIErrorResponseShape(t *testing.T) {
	server, cleanup := setupHandlerTestServer(t)
	defer cleanup()

	if server == nil {
		t.Skip("Server setup failed")
	}

	// Test error response structure consistency
	t.Run("error_responses_are_parseable", func(t *testing.T) {
		endpoints := []struct {
			method string
			path   string
			body   map[string]interface{}
		}{
			{"POST", "/api/v1/auth/register", map[string]interface{}{}},
			{"POST", "/api/v1/auth/login", map[string]interface{}{}},
		}

		for _, ep := range endpoints {
			t.Run(ep.method+" "+ep.path, func(t *testing.T) {
				body, _ := json.Marshal(ep.body)
				req, _ := http.NewRequest(ep.method, server.URL+ep.path, bytes.NewReader(body))
				req.Header.Set("Content-Type", "application/json")

				resp, err := http.DefaultClient.Do(req)
				if err != nil {
					t.Fatalf("Request failed: %v", err)
				}
				defer resp.Body.Close()

				// Only check error responses (4xx/5xx)
				if resp.StatusCode >= 200 && resp.StatusCode < 400 {
					return
				}

				respBody, _ := io.ReadAll(resp.Body)
				var result map[string]interface{}
				if err := json.Unmarshal(respBody, &result); err != nil {
					t.Logf("Non-JSON error response: %s", string(respBody))
				} else {
					t.Logf("Error response: %+v", result)
				}
			})
		}
	})
}

// =============================================================================
// Response Structure Tests for Success Cases
// =============================================================================

func TestAPIResponseSuccessStructure(t *testing.T) {
	server, cleanup := setupHandlerTestServer(t)
	defer cleanup()

	if server == nil {
		t.Skip("Server setup failed")
	}

	// Create a user first
	regBody, _ := json.Marshal(map[string]interface{}{
		"username": "contractuser",
		"password": "TestPass123!",
	})
	regReq, _ := http.NewRequest("POST", server.URL+"/api/v1/auth/register", bytes.NewReader(regBody))
	regReq.Header.Set("Content-Type", "application/json")
	regResp, _ := http.DefaultClient.Do(regReq)
	io.ReadAll(regResp.Body)
	regResp.Body.Close()

	// Login to get token
	loginBody, _ := json.Marshal(map[string]interface{}{
		"account":  "contractuser",
		"password": "TestPass123!",
	})
	loginReq, _ := http.NewRequest("POST", server.URL+"/api/v1/auth/login", bytes.NewReader(loginBody))
	loginReq.Header.Set("Content-Type", "application/json")
	loginResp, err := http.DefaultClient.Do(loginReq)
	if err != nil {
		t.Fatalf("Login failed: %v", err)
	}
	var loginResult map[string]interface{}
	json.NewDecoder(loginResp.Body).Decode(&loginResult)
	loginResp.Body.Close()

	accessToken, ok := loginResult["access_token"].(string)
	if !ok {
		t.Skip("Could not get access token")
	}

	t.Run("user_info_response", func(t *testing.T) {
		req, _ := http.NewRequest("GET", server.URL+"/api/v1/auth/me", nil)
		req.Header.Set("Authorization", "Bearer "+accessToken)

		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			t.Fatalf("Request failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Skipf("User info endpoint returned %d", resp.StatusCode)
		}

		var result map[string]interface{}
		if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
			t.Fatalf("Response should be valid JSON: %v", err)
		}

		// Log the structure
		t.Logf("User info response: %+v", result)

		// Verify standard user info fields
		requiredFields := []string{"id", "username", "status"}
		for _, field := range requiredFields {
			if _, ok := result[field]; !ok {
				t.Errorf("Response should have '%s' field", field)
			}
		}
	})
}
