package httpserver_test

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	apicontract "bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/contracts/api"
	httpserver "bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/http"
)

type mockIndexedRepoLister struct {
	repos []apicontract.IndexedRepoResponse
	err   error
	lastOrgID string
}

func (m *mockIndexedRepoLister) ListIndexedRepos(orgID string) ([]apicontract.IndexedRepoResponse, error) {
	m.lastOrgID = orgID
	if m.err != nil {
		return nil, m.err
	}
	return m.repos, nil
}

const testGatewayInternalSecret = "test-internal-secret"

func withIndexedReposAuth(req *http.Request) {
	req.Header.Set("X-Internal-Secret", testGatewayInternalSecret)
}

func TestListIndexedReposUnauthorized(t *testing.T) {
	lister := &mockIndexedRepoLister{repos: []apicontract.IndexedRepoResponse{}}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, nil, nil, lister, nil, testGatewayInternalSecret)

	req := httptest.NewRequest(http.MethodGet, "/indexed-repos", nil)
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusUnauthorized {
		t.Fatalf("status: got %d body=%s", rec.Code, rec.Body.String())
	}
	if lister.lastOrgID != "" {
		t.Fatalf("expected no lister call without secret, got org %q", lister.lastOrgID)
	}
}

func TestListIndexedReposSuccess(t *testing.T) {
	lastIndexed := "2026-06-17T14:00:00Z"
	lister := &mockIndexedRepoLister{
		repos: []apicontract.IndexedRepoResponse{
			{
				RepoID:        "ad/example-repo.com",
				RepoName:      "example-repo.com",
				GitURL:        "https://bit.admedia.com/scm/ad/example-repo.com.git",
				Status:        apicontract.IndexedRepoStatusReady,
				LastIndexedAt: &lastIndexed,
			},
		},
	}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, nil, nil, lister, nil, testGatewayInternalSecret)

	req := httptest.NewRequest(http.MethodGet, "/indexed-repos", nil)
	withIndexedReposAuth(req)
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", rec.Code, rec.Body.String())
	}
	if lister.lastOrgID != "" {
		t.Fatalf("expected empty org filter, got %q", lister.lastOrgID)
	}

	var resp apicontract.IndexedReposResponse
	if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if len(resp.Repos) != 1 {
		t.Fatalf("expected 1 repo, got %d", len(resp.Repos))
	}
}

func TestListIndexedReposByOrgHeader(t *testing.T) {
	lister := &mockIndexedRepoLister{repos: []apicontract.IndexedRepoResponse{}}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, nil, nil, lister, nil, testGatewayInternalSecret)

	req := httptest.NewRequest(http.MethodGet, "/indexed-repos", nil)
	withIndexedReposAuth(req)
	req.Header.Set("X-Org-Id", "org-123")
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("status: got %d", rec.Code)
	}
	if lister.lastOrgID != "org-123" {
		t.Fatalf("expected org-123, got %q", lister.lastOrgID)
	}
}

func TestListIndexedReposGatewayRequiresOrg(t *testing.T) {
	lister := &mockIndexedRepoLister{repos: []apicontract.IndexedRepoResponse{}}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, nil, nil, lister, nil, testGatewayInternalSecret)

	req := httptest.NewRequest(http.MethodGet, "/indexed-repos", nil)
	withIndexedReposAuth(req)
	req.Header.Set("X-Gateway-Indexed-Repos", "1")
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusBadRequest {
		t.Fatalf("status: got %d body=%s", rec.Code, rec.Body.String())
	}
	if lister.lastOrgID != "" {
		t.Fatalf("expected no lister call without org, got org %q", lister.lastOrgID)
	}
}

func TestListIndexedReposEmpty(t *testing.T) {
	lister := &mockIndexedRepoLister{repos: []apicontract.IndexedRepoResponse{}}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, nil, nil, lister, nil, testGatewayInternalSecret)

	req := httptest.NewRequest(http.MethodGet, "/indexed-repos", nil)
	withIndexedReposAuth(req)
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("status: got %d", rec.Code)
	}

	var resp apicontract.IndexedReposResponse
	if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if resp.Repos == nil || len(resp.Repos) != 0 {
		t.Fatalf("expected empty repos array, got %+v", resp.Repos)
	}
}

func TestListIndexedReposError(t *testing.T) {
	lister := &mockIndexedRepoLister{err: errors.New("store unavailable")}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, nil, nil, lister, nil, testGatewayInternalSecret)

	req := httptest.NewRequest(http.MethodGet, "/indexed-repos", nil)
	withIndexedReposAuth(req)
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusInternalServerError {
		t.Fatalf("status: got %d", rec.Code)
	}
}
