package rest_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/go-chi/chi/v5"

	"github.com/pingcap/agent-git-service/internal/crypto"
	"github.com/pingcap/agent-git-service/internal/db"
	"github.com/pingcap/agent-git-service/internal/embedding"
	"github.com/pingcap/agent-git-service/internal/githttp"
	"github.com/pingcap/agent-git-service/internal/gitstore"
	"github.com/pingcap/agent-git-service/internal/graphql"
	"github.com/pingcap/agent-git-service/internal/oauth"
	rest "github.com/pingcap/agent-git-service/internal/rest"
	"github.com/pingcap/agent-git-service/internal/router"
	"github.com/pingcap/agent-git-service/internal/service"
	"github.com/pingcap/agent-git-service/internal/tenant"
	"github.com/pingcap/agent-git-service/internal/testharness/testdb"
	"github.com/pingcap/agent-git-service/internal/wikicatalog"
)

type prQueryTenantProvider struct {
	resources *service.TenantResources
}

func (p prQueryTenantProvider) ForTenant(id tenant.ID) (*service.TenantResources, error) {
	if p.resources == nil || id != p.resources.Info.ID {
		return nil, fmt.Errorf("unknown PR query tenant %q", id)
	}
	return p.resources, nil
}

func TestCreatePR_QueryCount(t *testing.T) {
	tmpDir := t.TempDir()
	logger := newQueryCounterLogger()

	gdb, cleanup := testdb.Open(t, testdb.Options{Prefix: "rest_pr_query", Logger: logger})
	t.Cleanup(cleanup)
	if err := db.Migrate(gdb); err != nil {
		t.Fatalf("migrate: %v", err)
	}

	info := tenant.Info{
		ID:               "rest-pr-query",
		CanonicalBaseURL: "https://rest-pr-query.example.test",
	}
	ctx := tenant.WithInfo(t.Context(), info)
	store, err := gitstore.NewForTenant(tmpDir, info.ID)
	if err != nil {
		t.Fatalf("gitstore: %v", err)
	}
	blobStore := wikicatalog.NewBlobStore(tmpDir)
	secretBox, err := crypto.NewSecretBox("")
	if err != nil {
		t.Fatalf("secret box: %v", err)
	}
	resources := &service.TenantResources{
		Info:        info,
		DB:          gdb,
		Git:         store,
		WikiCatalog: wikicatalog.New(gdb, blobStore),
		WikiBlob:    blobStore,
		SecretBox:   secretBox,
		Embedder:    embedding.NopEmbedder{},
	}
	if err := resources.Validate(); err != nil {
		t.Fatalf("validate tenant resources: %v", err)
	}
	svc, err := service.NewService(t.Context(), prQueryTenantProvider{resources: resources})
	if err != nil {
		t.Fatalf("service.NewService: %v", err)
	}
	user := db.User{Login: "testuser", Name: "testuser", Type: db.TypeUser, SiteAdmin: true}
	if err := gdb.WithContext(ctx).Create(&user).Error; err != nil {
		t.Fatalf("seed user: %v", err)
	}
	tokenValue := "query-token"
	if err := gdb.WithContext(ctx).Create(&db.Token{UserID: user.ID, Value: tokenValue}).Error; err != nil {
		t.Fatalf("seed token: %v", err)
	}

	handlers := &rest.Deps{Svc: svc}
	gqlSrv := graphql.NewServer(svc)
	gitHandler := githttp.New(store, svc)
	oauthHandler := oauth.New(svc)
	mux := router.RegisterRoutes(chi.NewRouter(), handlers, gitHandler, gqlSrv, oauthHandler)

	repo, err := svc.CreateRepo(ctx, service.CreateRepoInput{
		OwnerLogin: user.Login,
		Name:       "query-count-pr",
		AutoInit:   true,
	})
	if err != nil {
		t.Fatalf("CreateRepo: %v", err)
	}
	if err := store.CreateBranch(ctx, repo.FullName, "feature", "main"); err != nil {
		t.Fatalf("CreateBranch: %v", err)
	}
	if _, err := store.WriteFile(ctx, repo.FullName, "feature", "feature.txt", "add feature", []byte("hello feature")); err != nil {
		t.Fatalf("WriteFile: %v", err)
	}

	logger.Reset()

	body, err := json.Marshal(map[string]any{
		"title": "query count PR",
		"body":  "PR body",
		"head":  "feature",
		"base":  "main",
	})
	if err != nil {
		t.Fatalf("marshal request: %v", err)
	}

	req := httptest.NewRequest(http.MethodPost, "/api/v3/repos/testuser/query-count-pr/pulls", bytes.NewReader(body))
	req = req.WithContext(ctx)
	req.Header.Set("Authorization", "token "+tokenValue)
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, req)

	if rec.Code != http.StatusCreated {
		t.Fatalf("CreatePR status = %d, want %d, body=%s", rec.Code, http.StatusCreated, rec.Body.String())
	}

	queryCount := logger.Count()
	t.Logf("CreatePR query count: %d", queryCount)
	if queryCount > 30 {
		t.Fatalf("CreatePR query count = %d, want <= 30", queryCount)
	}
}
