package main

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

func TestRoles(t *testing.T) {
	app := newTestApplication(t)
	ts := newTestServer(t, app, routes(app))
	defer ts.Close()

	issueBody := map[string]any{
		"title":       "Role test issue",
		"description": "Testing role-based access.",
		"start_time":  time.Now().Format(time.RFC3339),
		"end_time":    time.Now().Add(24 * time.Hour).Format(time.RFC3339),
		"options":     []string{"Option A", "Option B"},
	}

	// Seed a contributor so we have an issue to read against.
	adminEmail := uniqueEmail()
	adminToken := ts.registerAndLogin(t, adminEmail, "pa$$word123")

	code, _, res := ts.postJSONWithToken(t, "/v1/issues", adminToken, issueBody)
	if code != http.StatusCreated {
		t.Fatalf("seed issue: want 201 got %d: %s", code, res)
	}

	// -------------------------------------------------------------------------
	// No role — all protected routes must return 403.
	// -------------------------------------------------------------------------
	t.Run("no role is forbidden", func(t *testing.T) {
		email := uniqueEmail()
		token := ts.registerAndLogin(t, email, "pa$$word123")

		user, err := ts.app.Models.Users.GetByEmail(email)
		if err != nil {
			t.Fatalf("look up user: %v", err)
		}
		if _, err = ts.app.Models.Roles.DB.Exec(`DELETE FROM users_roles WHERE user_id = $1`, user.ID); err != nil {
			t.Fatalf("strip roles: %v", err)
		}

		if code, _, _ := ts.getWithToken(t, "/v1/issues", token); code != http.StatusForbidden {
			t.Errorf("GET /v1/issues: want 403, got %d", code)
		}
		if code, _, _ := ts.postJSONWithToken(t, "/v1/issues", token, issueBody); code != http.StatusForbidden {
			t.Errorf("POST /v1/issues: want 403, got %d", code)
		}
	})

	// -------------------------------------------------------------------------
	// Viewer — can read, cannot write.
	// -------------------------------------------------------------------------
	t.Run("viewer can read but not write", func(t *testing.T) {
		email := uniqueEmail()
		token := ts.registerAndLogin(t, email, "pa$$word123")
		ts.setRole(t, email, "viewer")

		if code, _, _ := ts.getWithToken(t, "/v1/issues", token); code != http.StatusOK {
			t.Errorf("GET /v1/issues: want 200, got %d", code)
		}
		if code, _, _ := ts.postJSONWithToken(t, "/v1/issues", token, issueBody); code != http.StatusForbidden {
			t.Errorf("POST /v1/issues: want 403, got %d", code)
		}
	})

	// -------------------------------------------------------------------------
	// Contributor — full read/write access.
	// -------------------------------------------------------------------------
	t.Run("contributor can read and write", func(t *testing.T) {
		email := uniqueEmail()
		token := ts.registerAndLogin(t, email, "pa$$word123")
		ts.setRole(t, email, "contributor")

		if code, _, _ := ts.getWithToken(t, "/v1/issues", token); code != http.StatusOK {
			t.Errorf("GET /v1/issues: want 200, got %d", code)
		}
		if code, _, _ := ts.postJSONWithToken(t, "/v1/issues", token, issueBody); code != http.StatusCreated {
			t.Errorf("POST /v1/issues: want 201, got %d", code)
		}
	})

	// -------------------------------------------------------------------------
	// Admin — same permissions as contributor for now.
	// -------------------------------------------------------------------------
	t.Run("admin can read and write", func(t *testing.T) {
		email := uniqueEmail()
		token := ts.registerAndLogin(t, email, "pa$$word123")
		ts.setRole(t, email, "admin")

		if code, _, _ := ts.getWithToken(t, "/v1/issues", token); code != http.StatusOK {
			t.Errorf("GET /v1/issues: want 200, got %d", code)
		}
		if code, _, _ := ts.postJSONWithToken(t, "/v1/issues", token, issueBody); code != http.StatusCreated {
			t.Errorf("POST /v1/issues: want 201, got %d", code)
		}
	})
}
