package meta

import (
	"encoding/json"
	"net/http"
	"path/filepath"
	"slices"
	"testing"

	"nadir/internal/auth"
	"nadir/internal/module"
	"nadir/internal/rbac"

	"github.com/danielgtaylor/huma/v2"
	"github.com/danielgtaylor/huma/v2/adapters/humago"
	"github.com/danielgtaylor/huma/v2/humatest"
)

type dummyModule struct {
	id    string
	perms []rbac.Permission
}

func (m *dummyModule) ID() string                     { return m.id }
func (m *dummyModule) Name() string                   { return m.id }
func (m *dummyModule) Permissions() []rbac.Permission { return m.perms }
func (m *dummyModule) Register(api huma.API)          {}

func TestWhoami(t *testing.T) {
	tempDir := t.TempDir()
	sessions, err := auth.NewSessionStore(filepath.Join(tempDir, "sessions.db"))
	if err != nil {
		t.Fatal(err)
	}

	tokenStore, err := auth.NewTokenStore(filepath.Join(tempDir, "tokens.db"))
	if err != nil {
		t.Fatal(err)
	}
	defer tokenStore.Close()
	tokenAuth := auth.NewTokenAuth(tokenStore)

	roles := rbac.New()
	roles.DefineRole(rbac.Role{
		Name: "admin-role",
		ModuleGrants: map[string][]rbac.Permission{
			"system": {rbac.Read},
		},
	})
	roles.AssignRole("admin", "admin-role")

	mods := []module.Module{
		&dummyModule{
			id:    "system",
			perms: []rbac.Permission{rbac.Read, rbac.Write},
		},
	}

	mux := http.NewServeMux()
	api := humatest.Wrap(t, humago.New(mux, huma.DefaultConfig("Test", "1.0.0")))
	RegisterWhoami(api, sessions, tokenAuth, roles, mods)

	// 1. Unauthorized request (no token, no session)
	resp := api.Get("/api/whoami")
	if resp.Code != http.StatusUnauthorized {
		t.Errorf("expected 401, got %d", resp.Code)
	}

	// 2. Cookie session request
	sessToken, err := sessions.Create("admin")
	if err != nil {
		t.Fatal(err)
	}
	resp = api.Get("/api/whoami", "Cookie: nadir_session_id="+sessToken)
	if resp.Code != http.StatusOK {
		t.Errorf("expected 200, got %d", resp.Code)
	}
	var body WhoamiBody
	if err := json.Unmarshal(resp.Body.Bytes(), &body); err != nil {
		t.Fatal(err)
	}
	if body.Username != "admin" {
		t.Errorf("expected username admin, got %q", body.Username)
	}
	if !slices.Contains(body.Permissions["system"], "read") {
		t.Errorf("expected system read permission, got %v", body.Permissions["system"])
	}

	// 3. Token request
	bearerToken, err := tokenStore.Create("api-user")
	if err != nil {
		t.Fatal(err)
	}
	roles.DefineRole(rbac.Role{
		Name: "api-role",
		ModuleGrants: map[string][]rbac.Permission{
			"system": {rbac.Write},
		},
	})
	roles.AssignRole("api-user", "api-role")

	resp = api.Get("/api/whoami", "Authorization: Bearer "+bearerToken)
	if resp.Code != http.StatusOK {
		t.Errorf("expected 200, got %d", resp.Code)
	}
	if err := json.Unmarshal(resp.Body.Bytes(), &body); err != nil {
		t.Fatal(err)
	}
	if body.Username != "api-user" {
		t.Errorf("expected username api-user, got %q", body.Username)
	}
	if !slices.Contains(body.Permissions["system"], "write") {
		t.Errorf("expected system write permission, got %v", body.Permissions["system"])
	}
}
