package response

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

	"github.com/gofiber/fiber/v2"
)

func parseBody(t *testing.T, resp *http.Response) map[string]any {
	t.Helper()

	var body map[string]any
	if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
		t.Fatalf("failed to parse response body: %v", err)
	}
	return body
}

func TestErrorWithDetailsResponseShape(t *testing.T) {
	app := fiber.New()
	app.Get("/test", func(c *fiber.Ctx) error {
		return ErrorWithDetails(
			c,
			fiber.StatusConflict,
			"conflict",
			"resource already exists",
			map[string]any{"field": "email"},
		)
	})

	req := httptest.NewRequest(http.MethodGet, "/test", nil)
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("request failed: %v", err)
	}

	if resp.StatusCode != http.StatusConflict {
		t.Fatalf("unexpected status code: %d", resp.StatusCode)
	}

	body := parseBody(t, resp)
	if body["error"] != "resource already exists" {
		t.Fatalf("unexpected error value: %v", body["error"])
	}
	if body["code"] != "conflict" {
		t.Fatalf("unexpected code value: %v", body["code"])
	}

	details, ok := body["details"].(map[string]any)
	if !ok {
		t.Fatalf("details should be map, got: %T", body["details"])
	}
	if details["field"] != "email" {
		t.Fatalf("unexpected details value: %v", details["field"])
	}
}

func TestStatusCodeMapping(t *testing.T) {
	tests := []struct {
		name     string
		status   int
		expected string
	}{
		{name: "bad request", status: fiber.StatusBadRequest, expected: "bad_request"},
		{name: "unauthorized", status: fiber.StatusUnauthorized, expected: "invalid_session"},
		{name: "forbidden", status: fiber.StatusForbidden, expected: "forbidden"},
		{name: "not found", status: fiber.StatusNotFound, expected: "not_found"},
		{name: "conflict", status: fiber.StatusConflict, expected: "conflict"},
		{name: "too many requests", status: fiber.StatusTooManyRequests, expected: "rate_limited"},
		{name: "service unavailable", status: fiber.StatusServiceUnavailable, expected: "service_unavailable"},
		{name: "fallback", status: fiber.StatusInternalServerError, expected: "internal_error"},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got := StatusCode(tc.status)
			if got != tc.expected {
				t.Fatalf("unexpected code: got=%s expected=%s", got, tc.expected)
			}
		})
	}
}
