package api

import (
	"net/http"
	"testing"
	"time"

	"github.com/gofiber/fiber/v2"

	"wucher/internal/config"
	"wucher/internal/service"
	"wucher/internal/transport/http/handlers"
)

func TestForgotPasswordIPLimiter(t *testing.T) {
	cfg := config.AuthConfig{
		ForgotPasswordIPRateMax:    1,
		ForgotPasswordIPRateWindow: time.Minute,
	}
	svc := service.NewAuthService(nil, nil, nil, nil, service.AuthServiceDependencies{SSO: nil, Protector: nil, Config: cfg})

	app := fiber.New()
	app.Post("/forgot", forgotPasswordIPLimiter(svc), func(c *fiber.Ctx) error {
		return c.SendStatus(http.StatusOK)
	})

	req1, _ := http.NewRequest(http.MethodPost, "/forgot", nil)
	resp1, err := app.Test(req1)
	if err != nil {
		t.Fatalf("first request failed: %v", err)
	}
	if resp1.StatusCode != http.StatusOK {
		t.Fatalf("expected first request 200, got %d", resp1.StatusCode)
	}

	req2, _ := http.NewRequest(http.MethodPost, "/forgot", nil)
	resp2, err := app.Test(req2)
	if err != nil {
		t.Fatalf("second request failed: %v", err)
	}
	if resp2.StatusCode != http.StatusTooManyRequests {
		t.Fatalf("expected second request 429, got %d", resp2.StatusCode)
	}
}

func TestLoginIPLimiter(t *testing.T) {
	cfg := config.AuthConfig{
		LoginIPRateMax:    1,
		LoginIPRateWindow: time.Minute,
	}
	svc := service.NewAuthService(nil, nil, nil, nil, service.AuthServiceDependencies{SSO: nil, Protector: nil, Config: cfg})

	app := fiber.New()
	app.Post("/login", loginIPLimiter(svc), func(c *fiber.Ctx) error {
		return c.SendStatus(http.StatusOK)
	})

	req1, _ := http.NewRequest(http.MethodPost, "/login", nil)
	resp1, err := app.Test(req1)
	if err != nil {
		t.Fatalf("first request failed: %v", err)
	}
	if resp1.StatusCode != http.StatusOK {
		t.Fatalf("expected first request 200, got %d", resp1.StatusCode)
	}

	req2, _ := http.NewRequest(http.MethodPost, "/login", nil)
	resp2, err := app.Test(req2)
	if err != nil {
		t.Fatalf("second request failed: %v", err)
	}
	if resp2.StatusCode != http.StatusTooManyRequests {
		t.Fatalf("expected second request 429, got %d", resp2.StatusCode)
	}
}

func TestRegisterAuthRoutes(t *testing.T) {
	app := fiber.New()
	v1 := app.Group("/api/v1")

	svc := service.NewAuthService(nil, nil, nil, nil, service.AuthServiceDependencies{SSO: nil, Protector: nil, Config: config.AuthConfig{}})
	authHandler := handlers.NewAuthHandler(svc)
	registerAuthRoutes(v1, authHandler, svc, nil)

	routes := app.GetRoutes(true)
	required := []struct {
		method string
		path   string
	}{
		{method: http.MethodPost, path: "/api/v1/auth/register"},
		{method: http.MethodPost, path: "/api/v1/auth/invite"},
		{method: http.MethodPost, path: "/api/v1/auth/invite/resend-set-password"},
		{method: http.MethodPost, path: "/api/v1/auth/forgot-password"},
		{method: http.MethodPost, path: "/api/v1/auth/reset-password"},
		{method: http.MethodPost, path: "/api/v1/auth/pin/setup"},
		{method: http.MethodPost, path: "/api/v1/auth/pin/verify"},
		{method: http.MethodPost, path: "/api/v1/auth/pin/change"},
		{method: http.MethodPost, path: "/api/v1/auth/pin/request-reset"},
		{method: http.MethodPost, path: "/api/v1/auth/pin/forgot"},
		{method: http.MethodPost, path: "/api/v1/auth/pin/reset"},
		{method: http.MethodPost, path: "/api/v1/auth/login"},
		{method: http.MethodPost, path: "/api/v1/auth/exchange"},
		{method: http.MethodPost, path: "/api/v1/auth/webauthn/register/begin"},
		{method: http.MethodPost, path: "/api/v1/auth/webauthn/register/finish"},
		{method: http.MethodPost, path: "/api/v1/auth/webauthn/login/begin"},
		{method: http.MethodPost, path: "/api/v1/auth/webauthn/login/finish"},
		{method: http.MethodPost, path: "/api/v1/auth/webauthn/reset-setup"},
		{method: http.MethodPost, path: "/api/v1/auth/refresh"},
		{method: http.MethodGet, path: "/api/v1/auth/sessions"},
		{method: http.MethodDelete, path: "/api/v1/auth/sessions/:session_id"},
		{method: http.MethodGet, path: "/api/v1/auth/me"},
		{method: http.MethodGet, path: "/api/v1/auth/microsoft/login"},
		{method: http.MethodGet, path: "/api/v1/auth/microsoft/login-check"},
		{method: http.MethodGet, path: "/api/v1/auth/microsoft/link"},
		{method: http.MethodPost, path: "/api/v1/auth/sso/link"},
		{method: http.MethodDelete, path: "/api/v1/auth/sso/unlink"},
		{method: http.MethodPost, path: "/api/v1/auth/totp/verify"},
		{method: http.MethodPost, path: "/api/v1/auth/totp/reset-setup"},
	}

	for _, tc := range required {
		if !hasRoute(routes, tc.method, tc.path) {
			t.Fatalf("route %s %s not registered", tc.method, tc.path)
		}
	}

	var forgotRoute *fiber.Route
	for i := range routes {
		if routes[i].Method == http.MethodPost && routes[i].Path == "/api/v1/auth/forgot-password" {
			forgotRoute = &routes[i]
			break
		}
	}
	if forgotRoute == nil {
		t.Fatalf("forgot password route not found")
	}
	if len(forgotRoute.Handlers) < 2 {
		t.Fatalf("expected forgot password route to have limiter and handler, got %d handlers", len(forgotRoute.Handlers))
	}

	var loginRoute *fiber.Route
	for i := range routes {
		if routes[i].Method == http.MethodPost && routes[i].Path == "/api/v1/auth/login" {
			loginRoute = &routes[i]
			break
		}
	}
	if loginRoute == nil {
		t.Fatalf("login route not found")
	}
	if len(loginRoute.Handlers) < 2 {
		t.Fatalf("expected login route to have limiter and handler, got %d handlers", len(loginRoute.Handlers))
	}
}
