package questions

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

	"boostai-backend/internal/http/respond"
	"boostai-backend/internal/questiongen"
	"boostai-backend/internal/sqlc"

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

func TestGenerateQuestionsReturnsGeneratorUnavailable(t *testing.T) {
	t.Parallel()

	handler := NewHandler(nil, nil)
	status, body := performGenerateRequest(t, handler, map[string]any{
		"topic":      "fractions",
		"difficulty": "easy",
		"count":      1,
	}, true)

	if status != fiber.StatusServiceUnavailable {
		t.Fatalf("expected status %d, got %d", fiber.StatusServiceUnavailable, status)
	}
	if body.Error != "generator_unavailable" {
		t.Fatalf("expected generator_unavailable error, got %#v", body)
	}
}

func TestGenerateQuestionsRequiresTeacherAuthentication(t *testing.T) {
	t.Parallel()

	handler := NewHandler(nil, questiongen.NewService())
	status, body := performGenerateRequest(t, handler, map[string]any{
		"topic":      "fractions",
		"difficulty": "easy",
		"count":      1,
	}, false)

	if status != fiber.StatusUnauthorized {
		t.Fatalf("expected status %d, got %d", fiber.StatusUnauthorized, status)
	}
	if body.Error != "unauthorized" {
		t.Fatalf("expected unauthorized error, got %#v", body)
	}
}

func TestGenerateQuestionsRejectsZeroCount(t *testing.T) {
	t.Parallel()

	handler := NewHandler(nil, questiongen.NewService())
	status, body := performGenerateRequest(t, handler, map[string]any{
		"topic":      "fractions",
		"difficulty": "easy",
		"count":      0,
	}, true)

	if status != fiber.StatusBadRequest {
		t.Fatalf("expected status %d, got %d", fiber.StatusBadRequest, status)
	}
	if body.Message != "count must be between 1 and 25" {
		t.Fatalf("expected count validation message, got %#v", body)
	}
}

func TestGenerateQuestionsRejectsInvalidStatus(t *testing.T) {
	t.Parallel()

	handler := NewHandler(nil, questiongen.NewService())
	status, body := performGenerateRequest(t, handler, map[string]any{
		"topic":      "fractions",
		"difficulty": "easy",
		"count":      1,
		"status":     "invalid",
	}, true)

	if status != fiber.StatusBadRequest {
		t.Fatalf("expected status %d, got %d", fiber.StatusBadRequest, status)
	}
	if body.Message != "status must be draft, published, or archived" {
		t.Fatalf("expected invalid status message, got %#v", body)
	}
}

func TestGenerateQuestionsRejectsInvalidTopic(t *testing.T) {
	t.Parallel()

	handler := NewHandler(nil, questiongen.NewService())
	status, body := performGenerateRequest(t, handler, map[string]any{
		"topic":      "not_a_topic",
		"difficulty": "easy",
		"count":      1,
	}, true)

	if status != fiber.StatusBadRequest {
		t.Fatalf("expected status %d, got %d", fiber.StatusBadRequest, status)
	}
	if body.Error != "invalid_request" {
		t.Fatalf("expected invalid_request error, got %#v", body)
	}
}

func performGenerateRequest(t *testing.T, handler *Handler, payload map[string]any, authenticated bool) (int, respond.ErrorBody) {
	t.Helper()

	app := fiber.New()
	app.Post("/questions/generate", func(c *fiber.Ctx) error {
		if authenticated {
			c.Locals("auth.user_id", int64(42))
			c.Locals("auth.role", sqlc.UserRoleTeacher)
		}
		return handler.GenerateQuestions(c)
	})

	bodyBytes, err := json.Marshal(payload)
	if err != nil {
		t.Fatalf("marshal payload: %v", err)
	}

	req := httptest.NewRequest(http.MethodPost, "/questions/generate", bytes.NewReader(bodyBytes))
	req.Header.Set("Content-Type", "application/json")

	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("app.Test returned error: %v", err)
	}
	defer resp.Body.Close()

	var errorBody respond.ErrorBody
	if err := json.NewDecoder(resp.Body).Decode(&errorBody); err != nil {
		t.Fatalf("decode error response: %v", err)
	}

	return resp.StatusCode, errorBody
}
