package integration_test

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

	"galaxy/integration/testenv"
	usermodel "galaxy/model/user"
	"galaxy/transcoder"
)

// TestAdminUserSanctionPermanentBlock verifies that applying the
// `permanent_block` sanction through the admin endpoint cascades:
//   - the user's active session is revoked (subsequent gateway calls
//     fail Unauthenticated);
//   - send-email-code on the same email is rejected with the
//     standard error envelope.
func TestAdminUserSanctionPermanentBlock(t *testing.T) {
	plat := testenv.Bootstrap(t, testenv.BootstrapOptions{})
	ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
	defer cancel()

	const email = "pilot+sanction@example.com"
	sess := testenv.RegisterSession(t, plat, email)
	gw, err := sess.DialAuthenticated(ctx, plat)
	if err != nil {
		t.Fatalf("dial: %v", err)
	}
	defer gw.Close()

	// Sanity: signed call works pre-sanction.
	payload, err := transcoder.GetMyAccountRequestToPayload(&usermodel.GetMyAccountRequest{})
	if err != nil {
		t.Fatalf("encode payload: %v", err)
	}
	if _, err := gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{}); err != nil {
		t.Fatalf("pre-sanction: %v", err)
	}

	userID, err := sess.LookupUserID(ctx, plat)
	if err != nil {
		t.Fatalf("resolve user_id: %v", err)
	}

	// Admin applies permanent_block.
	admin := testenv.NewBackendAdminClient(plat.Backend.HTTPURL, plat.Backend.AdminUser, plat.Backend.AdminPassword)
	body := map[string]any{
		"sanction_code": "permanent_block",
		"scope":         "global",
		"reason_code":   "tos_violation",
		"actor":         map[string]any{"type": "admin", "id": plat.Backend.AdminUser},
	}
	raw, resp, err := admin.Do(ctx, http.MethodPost, "/api/v1/admin/users/"+userID+"/sanctions", body)
	if err != nil || resp.StatusCode/100 != 2 {
		t.Fatalf("apply sanction: err=%v status=%d body=%s", err, resp.StatusCode, string(raw))
	}

	// Subsequent authenticated calls must fail.
	deadline := time.Now().Add(2 * time.Second)
	var lastErr error
	for time.Now().Before(deadline) {
		_, lastErr = gw.Execute(ctx, usermodel.MessageTypeGetMyAccount, payload, testenv.ExecuteOptions{})
		if lastErr != nil {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
	if lastErr == nil {
		t.Fatalf("authenticated call succeeded after permanent_block")
	}
	// Gateway maps a revoked session to FailedPrecondition ("device
	// session is revoked"); a session that vanished from the cache
	// before the call lands as Unauthenticated. Either is a correct
	// rejection.
	if !testenv.IsFailedPrecondition(lastErr) && !testenv.IsUnauthenticated(lastErr) {
		t.Fatalf("post-sanction status: %v", lastErr)
	}

	// New send-email-code on the same email must be rejected.
	public := testenv.NewPublicRESTClient(plat.Gateway.HTTPURL)
	_, _, err = public.SendEmailCode(ctx, email, "")
	if err == nil {
		t.Fatalf("send-email-code accepted for permanently blocked email")
	}
}
