package api

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

	"github.com/go-chi/chi/v5"

	"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
)

func newAdminInvitesRouter(h *handlers) chi.Router {
	r := chi.NewRouter()
	r.Route("/api/admin", func(admin chi.Router) {
		admin.Use(auth.RequireAdmin())
		admin.Get("/invites", h.handleListInvites)
		admin.Post("/invites", h.handleCreateInvite)
		admin.Delete("/invites/{token}", h.handleDeleteInvite)
	})
	return r
}

func TestAdminInvites_CreateListDelete(t *testing.T) {
	if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
		t.Skip("MINSTREL_TEST_DATABASE_URL not set")
	}
	h, pool := testHandlers(t)
	admin := seedUser(t, pool, "invadmin", "pw", true)

	// Create
	req := httptest.NewRequest(http.MethodPost, "/api/admin/invites", nil)
	req = withUser(req, admin)
	rec := httptest.NewRecorder()
	newAdminInvitesRouter(h).ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("create status = %d, want 200; body=%s", rec.Code, rec.Body.String())
	}
	var created struct {
		Token     string `json:"token"`
		ExpiresAt string `json:"expires_at"`
	}
	if err := json.Unmarshal(rec.Body.Bytes(), &created); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if created.Token == "" {
		t.Fatal("created.Token is empty")
	}

	// List
	req = httptest.NewRequest(http.MethodGet, "/api/admin/invites", nil)
	req = withUser(req, admin)
	rec = httptest.NewRecorder()
	newAdminInvitesRouter(h).ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("list status = %d, want 200", rec.Code)
	}
	var listed struct {
		Invites []struct{ Token string } `json:"invites"`
	}
	if err := json.Unmarshal(rec.Body.Bytes(), &listed); err != nil {
		t.Fatalf("decode: %v", err)
	}
	found := false
	for _, inv := range listed.Invites {
		if inv.Token == created.Token {
			found = true
		}
	}
	if !found {
		t.Errorf("created invite not in list response")
	}

	// Delete
	req = httptest.NewRequest(http.MethodDelete, "/api/admin/invites/"+created.Token, nil)
	req = withUser(req, admin)
	rec = httptest.NewRecorder()
	newAdminInvitesRouter(h).ServeHTTP(rec, req)
	if rec.Code != http.StatusNoContent {
		t.Errorf("delete status = %d, want 204", rec.Code)
	}

	// Verify gone
	var count int
	if err := pool.QueryRow(context.Background(),
		"SELECT count(*) FROM user_invites WHERE token = $1", created.Token).Scan(&count); err != nil {
		t.Fatalf("verify: %v", err)
	}
	if count != 0 {
		t.Errorf("invite still exists after delete (count=%d)", count)
	}
}

func TestAdminInvites_NonAdmin_403(t *testing.T) {
	if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
		t.Skip("MINSTREL_TEST_DATABASE_URL not set")
	}
	h, pool := testHandlers(t)
	user := seedUser(t, pool, "invnonadmin", "pw", false)

	req := httptest.NewRequest(http.MethodPost, "/api/admin/invites", nil)
	req = withUser(req, user)
	rec := httptest.NewRecorder()
	newAdminInvitesRouter(h).ServeHTTP(rec, req)
	if rec.Code != http.StatusForbidden {
		t.Errorf("status = %d, want 403", rec.Code)
	}
}
