package e2e

import (
	"encoding/json"
	"net/http"
	"testing"

	"goyco/internal/testutils"
)

func fetchSwaggerDoc(t *testing.T, ctx *testContext) map[string]any {
	t.Helper()

	request, err := http.NewRequest("GET", ctx.baseURL+"/swagger/doc.json", nil)
	if err != nil {
		t.Fatalf("Failed to create request: %v", err)
	}
	testutils.WithStandardHeaders(request)

	response, err := ctx.client.Do(request)
	if err != nil {
		t.Fatalf("Request failed: %v", err)
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		t.Skipf("Swagger JSON not available (status %d)", response.StatusCode)
		return nil
	}

	var swaggerDoc map[string]any
	if err := json.NewDecoder(response.Body).Decode(&swaggerDoc); err != nil {
		t.Fatalf("Failed to decode Swagger JSON: %v", err)
	}

	return swaggerDoc
}

func TestE2E_SwaggerDocumentation(t *testing.T) {
	ctx := setupTestContext(t)

	t.Run("swagger_json_is_valid", func(t *testing.T) {
		swaggerDoc := fetchSwaggerDoc(t, ctx)
		if swaggerDoc == nil {
			return
		}

		if swaggerDoc["swagger"] == nil && swaggerDoc["openapi"] == nil {
			t.Error("Swagger JSON missing swagger/openapi version")
		}

		if swaggerDoc["info"] == nil {
			t.Error("Swagger JSON missing info section")
		}

		if swaggerDoc["paths"] == nil {
			t.Error("Swagger JSON missing paths section")
		}
	})

	t.Run("swagger_yaml_is_valid", func(t *testing.T) {
		req, err := http.NewRequest("GET", ctx.baseURL+"/swagger/doc.yaml", nil)
		if err != nil {
			t.Fatalf("Failed to create request: %v", err)
		}
		testutils.WithStandardHeaders(req)

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

		if resp.StatusCode != http.StatusOK {
			t.Logf("Swagger YAML endpoint returned status %d (may not be available)", resp.StatusCode)
		}
	})

	t.Run("api_endpoints_documented", func(t *testing.T) {
		swaggerDoc := fetchSwaggerDoc(t, ctx)
		if swaggerDoc == nil {
			return
		}

		pathsRaw, exists := swaggerDoc["paths"]
		if !exists {
			t.Fatalf("Swagger doc missing 'paths' section")
		}
		paths, ok := pathsRaw.(map[string]any)
		if !ok {
			t.Fatalf("Swagger doc 'paths' section has invalid type: expected map[string]any, got %T", pathsRaw)
		}

		requiredPaths := []string{
			"/api",
			"/api/auth/login",
			"/api/auth/register",
			"/api/auth/me",
			"/api/auth/refresh",
			"/api/auth/revoke",
			"/api/auth/revoke-all",
			"/api/auth/logout",
			"/api/posts",
		}

		for _, requiredPath := range requiredPaths {
			if paths[requiredPath] == nil {
				t.Errorf("Required endpoint %s not documented", requiredPath)
			}
		}
	})

	t.Run("request_response_schemas_present", func(t *testing.T) {
		swaggerDoc := fetchSwaggerDoc(t, ctx)
		if swaggerDoc == nil {
			return
		}

		var definitions map[string]any
		var ok bool

		definitionsRaw, exists := swaggerDoc["definitions"]
		if exists {
			definitions, ok = definitionsRaw.(map[string]any)
			if !ok {
				t.Fatalf("Swagger doc 'definitions' section has invalid type: expected map[string]any, got %T", definitionsRaw)
			}
		} else {
			componentsRaw, exists := swaggerDoc["components"]
			if exists {
				components, ok := componentsRaw.(map[string]any)
				if !ok {
					t.Fatalf("Swagger doc 'components' section has invalid type: expected map[string]any, got %T", componentsRaw)
				}
				schemasRaw, exists := components["schemas"]
				if exists {
					definitions, ok = schemasRaw.(map[string]any)
					if !ok {
						t.Fatalf("Swagger doc 'components.schemas' section has invalid type: expected map[string]any, got %T", schemasRaw)
					}
				}
			}
		}

		if definitions == nil {
			t.Log("No definitions/schemas section found (may use inline schemas)")
			return
		}

		if len(definitions) == 0 {
			t.Error("Definitions/schemas section is empty")
		}
	})

	t.Run("swagger_ui_accessible", func(t *testing.T) {
		req, err := http.NewRequest("GET", ctx.baseURL+"/swagger/index.html", nil)
		if err != nil {
			t.Fatalf("Failed to create request: %v", err)
		}
		testutils.WithStandardHeaders(req)

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

		if resp.StatusCode != http.StatusOK {
			t.Logf("Swagger UI returned status %d (may not be available)", resp.StatusCode)
		}
	})
}

func TestE2E_APIEndpointDocumentation(t *testing.T) {
	ctx := setupTestContext(t)

	t.Run("api_info_endpoint_documented", func(t *testing.T) {
		swaggerDoc := fetchSwaggerDoc(t, ctx)
		if swaggerDoc == nil {
			return
		}

		pathsRaw, exists := swaggerDoc["paths"]
		if !exists {
			t.Fatalf("Swagger doc missing 'paths' section")
		}
		paths, ok := pathsRaw.(map[string]any)
		if !ok {
			t.Fatalf("Swagger doc 'paths' section has invalid type: expected map[string]any, got %T", pathsRaw)
		}

		apiPath, ok := paths["/api"].(map[string]any)
		if !ok {
			t.Error("API endpoint not documented")
			return
		}

		getMethod, ok := apiPath["get"].(map[string]any)
		if !ok {
			t.Error("API GET method not documented")
			return
		}

		if getMethod["responses"] == nil {
			t.Error("API endpoint missing responses")
		}
	})

	t.Run("auth_endpoints_documented", func(t *testing.T) {
		swaggerDoc := fetchSwaggerDoc(t, ctx)
		if swaggerDoc == nil {
			return
		}

		pathsRaw, exists := swaggerDoc["paths"]
		if !exists {
			t.Fatalf("Swagger doc missing 'paths' section")
		}
		paths, ok := pathsRaw.(map[string]any)
		if !ok {
			t.Fatalf("Swagger doc 'paths' section has invalid type: expected map[string]any, got %T", pathsRaw)
		}

		authEndpoints := []string{
			"/api/auth/login",
			"/api/auth/register",
			"/api/auth/refresh",
			"/api/auth/revoke",
			"/api/auth/revoke-all",
			"/api/auth/logout",
		}

		for _, endpoint := range authEndpoints {
			endpointData, ok := paths[endpoint].(map[string]any)
			if !ok {
				t.Errorf("Auth endpoint %s not documented", endpoint)
				continue
			}

			postMethod, ok := endpointData["post"].(map[string]any)
			if !ok {
				t.Errorf("Auth endpoint %s missing POST method", endpoint)
				continue
			}

			if postMethod["parameters"] == nil && postMethod["requestBody"] == nil {
				t.Logf("Auth endpoint %s may use inline request body", endpoint)
			}
		}

		refreshEndpointData, ok := paths["/api/auth/refresh"].(map[string]any)
		if ok {
			postMethod, ok := refreshEndpointData["post"].(map[string]any)
			if ok {
				responses, ok := postMethod["responses"].(map[string]any)
				if ok {
					successResponse, ok := responses["200"].(map[string]any)
					if ok {
						content, ok := successResponse["content"].(map[string]any)
						if ok {
							applicationJson, ok := content["application/json"].(map[string]any)
							if ok {
								schema, ok := applicationJson["schema"].(map[string]any)
								if ok {
									properties, ok := schema["properties"].(map[string]any)
									if !ok {
										data, ok := schema["data"].(map[string]any)
										if ok {
											properties, ok = data["properties"].(map[string]any)
										}
									}
									if properties != nil {
										if properties["refresh_token"] == nil {
											t.Error("Refresh endpoint response schema missing refresh_token field (rotation not documented)")
										}
									}
								}
							}
						}
					}
				}
			}
		}
	})
}
