//go:build integration

package inttest

import (
	"context"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"go.uber.org/zap/zaptest"

	"scrabble/backend/internal/account"
	"scrabble/backend/internal/game"
	"scrabble/backend/internal/server"
)

// TestConsoleBlockAndUnblock drives the admin console block flow through HTTP: blocking a user
// records the suspension and forfeits their active game, the user card renders both branches,
// and unblocking lifts it.
func TestConsoleBlockAndUnblock(t *testing.T) {
	ctx := context.Background()
	accounts := account.NewStore(testDB)
	games := newGameService()
	srv := consoleServer(t, accounts, games)

	gid, seats := newGameWithSeats(t, 2)
	blocked := seats[0]

	// The card renders the not-blocked branch before any block.
	if rec := consoleGet(t, srv, "/_gm/users/"+blocked.String()); rec.Code != http.StatusOK {
		t.Fatalf("user card before block = %d, want 200", rec.Code)
	}

	// Block permanently via the console.
	if rec := consolePost(t, srv, "/_gm/users/"+blocked.String()+"/block", url.Values{"duration": {"permanent"}}); rec.Code != http.StatusOK {
		t.Fatalf("console block = %d, want 200", rec.Code)
	}
	if _, ok, err := accounts.CurrentSuspension(ctx, blocked); err != nil || !ok {
		t.Fatalf("after console block: blocked=%v err=%v, want blocked", ok, err)
	}
	if g, err := games.GameByID(ctx, gid); err != nil || g.Status != game.StatusFinished {
		t.Fatalf("game after block: status=%q err=%v, want finished", g.Status, err)
	}

	// The card renders the blocked branch.
	if rec := consoleGet(t, srv, "/_gm/users/"+blocked.String()); rec.Code != http.StatusOK {
		t.Fatalf("user card after block = %d, want 200", rec.Code)
	}

	// Unblock via the console.
	if rec := consolePost(t, srv, "/_gm/users/"+blocked.String()+"/unblock", url.Values{}); rec.Code != http.StatusOK {
		t.Fatalf("console unblock = %d, want 200", rec.Code)
	}
	if _, ok, err := accounts.CurrentSuspension(ctx, blocked); err != nil || ok {
		t.Fatalf("after console unblock: blocked=%v err=%v, want not blocked", ok, err)
	}
}

// TestConsoleReasonsCRUD drives reason creation through the console and checks the page renders.
func TestConsoleReasonsCRUD(t *testing.T) {
	ctx := context.Background()
	accounts := account.NewStore(testDB)
	srv := consoleServer(t, accounts, newGameService())

	if rec := consoleGet(t, srv, "/_gm/reasons"); rec.Code != http.StatusOK {
		t.Fatalf("reasons page = %d, want 200", rec.Code)
	}
	form := url.Values{"text_en": {"Console reason"}, "text_ru": {"Причина из консоли"}}
	if rec := consolePost(t, srv, "/_gm/reasons", form); rec.Code != http.StatusOK {
		t.Fatalf("console create reason = %d, want 200", rec.Code)
	}
	reasons, err := accounts.ListReasons(ctx)
	if err != nil {
		t.Fatalf("list reasons: %v", err)
	}
	found := false
	for _, r := range reasons {
		if r.TextEn == "Console reason" && r.TextRu == "Причина из консоли" {
			found = true
		}
	}
	if !found {
		t.Error("reason created via the console was not found")
	}
}

// consoleServer assembles a server with the admin console mounted (it needs accounts, games and a
// registry).
func consoleServer(t *testing.T, accounts *account.Store, games *game.Service) *server.Server {
	t.Helper()
	return server.New(":0", server.Deps{
		Logger:   zaptest.NewLogger(t),
		DB:       testDB,
		Accounts: accounts,
		Games:    games,
		Registry: testRegistry,
		DictDir:  dictDir(),
	})
}

// consoleGet issues a console GET (a safe method, so the same-origin guard passes).
func consoleGet(t *testing.T, srv *server.Server, path string) *httptest.ResponseRecorder {
	t.Helper()
	rec := httptest.NewRecorder()
	srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "http://example.com"+path, nil))
	return rec
}

// consolePost issues a same-origin form POST to the console.
func consolePost(t *testing.T, srv *server.Server, path string, form url.Values) *httptest.ResponseRecorder {
	t.Helper()
	req := httptest.NewRequest(http.MethodPost, "http://example.com"+path, strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Origin", "http://example.com")
	rec := httptest.NewRecorder()
	srv.Handler().ServeHTTP(rec, req)
	return rec
}
