package oci

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

func TestTagsList(t *testing.T) {
	fdb := newFakeDB()
	fdb.addRepo("myrepo", 1)
	fdb.addTag(1, "latest")
	fdb.addTag(1, "v1.0")
	fdb.addTag(1, "v2.0")

	h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil)
	router := testRouter(h)

	req := authedRequest("GET", "/v2/myrepo/tags/list", nil)
	rr := httptest.NewRecorder()
	router.ServeHTTP(rr, req)

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

	var body tagListResponse
	if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
		t.Fatalf("decode body: %v", err)
	}
	if body.Name != "myrepo" {
		t.Fatalf("name: got %q, want %q", body.Name, "myrepo")
	}
	want := []string{"latest", "v1.0", "v2.0"}
	if len(body.Tags) != len(want) {
		t.Fatalf("tags count: got %d, want %d", len(body.Tags), len(want))
	}
	for i, tag := range body.Tags {
		if tag != want[i] {
			t.Fatalf("tags[%d]: got %q, want %q", i, tag, want[i])
		}
	}
}

func TestTagsListPagination(t *testing.T) {
	fdb := newFakeDB()
	fdb.addRepo("myrepo", 1)
	fdb.addTag(1, "alpha")
	fdb.addTag(1, "beta")
	fdb.addTag(1, "gamma")

	h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil)
	router := testRouter(h)

	// First page: n=2.
	req := authedRequest("GET", "/v2/myrepo/tags/list?n=2", nil)
	rr := httptest.NewRecorder()
	router.ServeHTTP(rr, req)

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

	var body tagListResponse
	if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
		t.Fatalf("decode body: %v", err)
	}
	if len(body.Tags) != 2 {
		t.Fatalf("page 1 tags count: got %d, want 2", len(body.Tags))
	}
	if body.Tags[0] != "alpha" || body.Tags[1] != "beta" {
		t.Fatalf("page 1 tags: got %v", body.Tags)
	}

	// Check Link header.
	link := rr.Header().Get("Link")
	if link == "" {
		t.Fatal("expected Link header for pagination")
	}

	// Second page: n=2, last=beta.
	req = authedRequest("GET", "/v2/myrepo/tags/list?n=2&last=beta", nil)
	rr = httptest.NewRecorder()
	router.ServeHTTP(rr, req)

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

	if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
		t.Fatalf("decode page 2: %v", err)
	}
	if len(body.Tags) != 1 {
		t.Fatalf("page 2 tags count: got %d, want 1", len(body.Tags))
	}
	if body.Tags[0] != "gamma" {
		t.Fatalf("page 2 tags: got %v", body.Tags)
	}

	// No Link header on last page.
	if link := rr.Header().Get("Link"); link != "" {
		t.Fatalf("expected no Link header on last page, got %q", link)
	}
}

func TestTagsListEmpty(t *testing.T) {
	fdb := newFakeDB()
	fdb.addRepo("myrepo", 1)
	// No tags.

	h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil)
	router := testRouter(h)

	req := authedRequest("GET", "/v2/myrepo/tags/list", nil)
	rr := httptest.NewRecorder()
	router.ServeHTTP(rr, req)

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

	var body tagListResponse
	if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
		t.Fatalf("decode body: %v", err)
	}
	if len(body.Tags) != 0 {
		t.Fatalf("expected empty tags, got %v", body.Tags)
	}
}

func TestTagsListRepoNotFound(t *testing.T) {
	fdb := newFakeDB()
	// No repos.

	h := NewHandler(fdb, newFakeBlobs(), allowAll(), nil)
	router := testRouter(h)

	req := authedRequest("GET", "/v2/nosuchrepo/tags/list", nil)
	rr := httptest.NewRecorder()
	router.ServeHTTP(rr, req)

	if rr.Code != http.StatusNotFound {
		t.Fatalf("status: got %d, want %d", rr.Code, http.StatusNotFound)
	}

	var body ociErrorResponse
	if err := json.NewDecoder(rr.Body).Decode(&body); err != nil {
		t.Fatalf("decode error body: %v", err)
	}
	if len(body.Errors) != 1 || body.Errors[0].Code != "NAME_UNKNOWN" {
		t.Fatalf("error code: got %+v, want NAME_UNKNOWN", body.Errors)
	}
}
