package reseed

import (
	"context"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"boostai-backend/internal/config"
	"boostai-backend/internal/seeddata"

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

func TestPageRequiresEnableFlag(t *testing.T) {
	t.Parallel()
	h := &Handler{cfg: &config.Config{Environment: "production"}}
	status, _ := performRequest(t, h, http.MethodGet, "/reseed", "", "")
	if status != fiber.StatusNotFound {
		t.Fatalf("expected 404, got %d", status)
	}
}

func TestLoginSetsCookieAndRedirects(t *testing.T) {
	t.Parallel()
	h := newTestHandler(func(ctx context.Context, mockDataDir string) (seeddata.Summary, error) {
		return seeddata.Summary{}, nil
	})
	status, resp := performRequest(t, h, http.MethodPost, "/reseed/login", "password=1588", "")
	if status != fiber.StatusSeeOther {
		t.Fatalf("expected 303, got %d", status)
	}
	if location := resp.Header.Get("Location"); location != "/reseed" {
		t.Fatalf("expected redirect to /reseed, got %q", location)
	}
	if cookie := resp.Header.Get("Set-Cookie"); !strings.Contains(cookie, reseedCookieName+"=") {
		t.Fatalf("expected auth cookie, got %q", cookie)
	}
}

func TestRunRequiresAuthCookie(t *testing.T) {
	t.Parallel()
	h := newTestHandler(func(ctx context.Context, mockDataDir string) (seeddata.Summary, error) {
		return seeddata.Summary{}, nil
	})
	status, body := performRequestBody(t, h, http.MethodPost, "/reseed/run", "", "")
	if status != fiber.StatusUnauthorized {
		t.Fatalf("expected 401, got %d", status)
	}
	if !strings.Contains(body, "Please unlock the reseed page first") {
		t.Fatalf("expected unlock message, got %q", body)
	}
}

func TestRunExecutesReseed(t *testing.T) {
	t.Parallel()
	h := newTestHandler(func(ctx context.Context, mockDataDir string) (seeddata.Summary, error) {
		if mockDataDir != "/app/Mock-Data" {
			t.Fatalf("expected mock data dir /app/Mock-Data, got %q", mockDataDir)
		}
		return seeddata.Summary{Users: 13, Assignments: 8, StudentAnswers: 588, MockDataDir: mockDataDir}, nil
	})
	status, body := performRequestBody(t, h, http.MethodPost, "/reseed/run", "", reseedCookieName+"="+h.authCookieValue())
	if status != fiber.StatusOK {
		t.Fatalf("expected 200, got %d", status)
	}
	if !strings.Contains(body, "Reseed completed") || !strings.Contains(body, "student_answers: 588") {
		t.Fatalf("expected success summary, got %q", body)
	}
}

func TestRunSurfacesRunnerError(t *testing.T) {
	t.Parallel()
	h := newTestHandler(func(ctx context.Context, mockDataDir string) (seeddata.Summary, error) {
		return seeddata.Summary{}, errors.New("boom")
	})
	status, body := performRequestBody(t, h, http.MethodPost, "/reseed/run", "", reseedCookieName+"="+h.authCookieValue())
	if status != fiber.StatusInternalServerError {
		t.Fatalf("expected 500, got %d", status)
	}
	if !strings.Contains(body, "boom") {
		t.Fatalf("expected error body, got %q", body)
	}
}

func newTestHandler(fn func(context.Context, string) (seeddata.Summary, error)) *Handler {
	return &Handler{
		cfg: &config.Config{
			Environment:        "production",
			AdminReseedEnabled: true,
			MockDataDir:        "/app/Mock-Data",
			JWTSecret:          "jwt-secret",
			ReseedPagePassword: "1588",
		},
		runner: fn,
	}
}

func performRequest(t *testing.T, handler *Handler, method, path, formBody, cookieHeader string) (int, *http.Response) {
	t.Helper()
	app := testApp(handler)
	req := httptest.NewRequest(method, path, strings.NewReader(formBody))
	if formBody != "" {
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	}
	if cookieHeader != "" {
		req.Header.Set("Cookie", cookieHeader)
	}
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("app.Test: %v", err)
	}
	return resp.StatusCode, resp
}

func performRequestBody(t *testing.T, handler *Handler, method, path, formBody, cookieHeader string) (int, string) {
	t.Helper()
	status, resp := performRequest(t, handler, method, path, formBody, cookieHeader)
	defer resp.Body.Close()
	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("ReadAll: %v", err)
	}
	return status, string(bodyBytes)
}

func testApp(handler *Handler) *fiber.App {
	app := fiber.New()
	app.Get("/reseed", handler.Page)
	app.Post("/reseed/login", handler.Login)
	app.Post("/reseed/run", handler.Run)
	app.Post("/reseed/logout", handler.Logout)
	return app
}
