//go:build integration

package integrationtest

import (
	"context"
	"testing"

	"github.com/ultisuite/ulti-backend/internal/auth"
	"github.com/ultisuite/ulti-backend/internal/permission"
	"github.com/ultisuite/ulti-backend/internal/users"
)

// RegularUser returns claims for a standard suite user.
func RegularUser(externalID string) *auth.Claims {
	return &auth.Claims{
		Sub:    externalID,
		Email:  externalID + "@test.ultimail.local",
		Name:   "Test User",
		Groups: []string{string(permission.RoleUser)},
	}
}

// AdminUser returns claims for a platform admin.
func AdminUser(externalID string) *auth.Claims {
	return &auth.Claims{
		Sub:    externalID,
		Email:  externalID + "@admin.ultimail.local",
		Name:   "Test Admin",
		Groups: []string{string(permission.RoleAdmin), "admin:write"},
	}
}

// EnsureUser provisions a user row directly (optional shortcut).
func EnsureUser(ctx context.Context, h *Harness, claims *auth.Claims) (string, error) {
	return users.EnsureUser(ctx, h.Pool, claims)
}

// DisableUser marks a user as disabled in the database.
func DisableUser(ctx context.Context, h *Harness, externalID string) error {
	_, err := h.Pool.Exec(ctx, `
		UPDATE users SET status = 'disabled', updated_at = NOW()
		WHERE external_id = $1
	`, externalID)
	return err
}

// RequireUserClient creates a regular user client, failing the test on error.
func RequireUserClient(t *testing.T, h *Harness) (*Client, *auth.Claims) {
	t.Helper()
	claims := RegularUser(NewExternalID("user"))
	client, err := h.Client(claims)
	if err != nil {
		t.Fatalf("client: %v", err)
	}
	return client, claims
}

// RequireAdminClient creates an admin user client.
func RequireAdminClient(t *testing.T, h *Harness) (*Client, *auth.Claims) {
	t.Helper()
	claims := AdminUser(NewExternalID("admin"))
	client, err := h.Client(claims)
	if err != nil {
		t.Fatalf("admin client: %v", err)
	}
	return client, claims
}

// SkipUnlessNextcloud skips the test when Nextcloud integration is not enabled.
func SkipUnlessNextcloud(t *testing.T) {
	t.Helper()
	env := LoadEnv()
	if !env.Nextcloud || env.NextcloudURL == "" {
		t.Skip("skipped: set ULTI_TEST_NEXTCLOUD=1 and ULTI_TEST_NEXTCLOUD_URL")
	}
}

// SkipUnlessImmich skips when Immich tests are not enabled.
func SkipUnlessImmich(t *testing.T) {
	t.Helper()
	env := LoadEnv()
	if !env.Immich || env.ImmichURL == "" {
		t.Skip("skipped: set ULTI_TEST_IMMICH=1 and ULTI_TEST_IMMICH_URL")
	}
}

// SkipUnlessJitsi skips when Jitsi tests are not enabled.
func SkipUnlessJitsi(t *testing.T) {
	t.Helper()
	if !LoadEnv().Jitsi {
		t.Skip("skipped: set ULTI_TEST_JITSI=1")
	}
}

// AssertUserProvisioned verifies the user exists after an API call.
func AssertUserProvisioned(t *testing.T, h *Harness, externalID string) {
	t.Helper()
	var id string
	err := h.Pool.QueryRow(context.Background(), `
		SELECT id FROM users WHERE external_id = $1
	`, externalID).Scan(&id)
	if err != nil {
		t.Fatalf("user %q not provisioned: %v", externalID, err)
	}
	if id == "" {
		t.Fatalf("empty user id for %q", externalID)
	}
}

func AssertErrorCode(t *testing.T, resp *Response, wantStatus int, wantCode string) {
	t.Helper()
	if resp.Status != wantStatus {
		t.Fatalf("status = %d, want %d; body = %s", resp.Status, wantStatus, string(resp.Body))
	}
	var body struct {
		Code string `json:"code"`
	}
	if err := resp.JSON(&body); err != nil {
		t.Fatalf("decode error body: %v", err)
	}
	if wantCode != "" && body.Code != wantCode {
		t.Fatalf("error code = %q, want %q; body = %s", body.Code, wantCode, string(resp.Body))
	}
}

func FailIf(err error, t *testing.T, msg string) {
	t.Helper()
	if err != nil {
		t.Fatalf("%s: %v", msg, err)
	}
}

func FailUnlessStatus(t *testing.T, resp *Response, want int) {
	t.Helper()
	if err := resp.MustStatus(want); err != nil {
		t.Fatal(err)
	}
}

// DecodeJSON decodes response body or fails the test.
func DecodeJSON(t *testing.T, resp *Response, v any) {
	t.Helper()
	if err := resp.JSON(v); err != nil {
		t.Fatalf("decode json: %v; body = %s", err, string(resp.Body))
	}
}

// FormatSub returns a readable subject for error messages.
func FormatSub(sub string) string {
	if sub == "" {
		return "<empty>"
	}
	return sub
}
