package oci

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

func TestBlobGet(t *testing.T) {
	fdb := newFakeDB()
	fdb.addRepo("myrepo", 1)
	fdb.addBlob(1, "sha256:layerdigest")

	blobs := newFakeBlobs()
	blobs.data["sha256:layerdigest"] = []byte("layer-content-bytes")

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

	req := authedRequest("GET", "/v2/myrepo/blobs/sha256:layerdigest", nil)
	rr := httptest.NewRecorder()
	router.ServeHTTP(rr, req)

	if rr.Code != http.StatusOK {
		t.Fatalf("status: got %d, want %d", rr.Code, http.StatusOK)
	}
	if ct := rr.Header().Get("Content-Type"); ct != "application/octet-stream" {
		t.Fatalf("Content-Type: got %q", ct)
	}
	if dcd := rr.Header().Get("Docker-Content-Digest"); dcd != "sha256:layerdigest" {
		t.Fatalf("Docker-Content-Digest: got %q", dcd)
	}
	if cl := rr.Header().Get("Content-Length"); cl != "19" {
		t.Fatalf("Content-Length: got %q, want %q", cl, "19")
	}
	if rr.Body.String() != "layer-content-bytes" {
		t.Fatalf("body: got %q", rr.Body.String())
	}
}

func TestBlobHead(t *testing.T) {
	fdb := newFakeDB()
	fdb.addRepo("myrepo", 1)
	fdb.addBlob(1, "sha256:layerdigest")

	blobs := newFakeBlobs()
	blobs.data["sha256:layerdigest"] = []byte("layer-content-bytes")

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

	req := authedRequest("HEAD", "/v2/myrepo/blobs/sha256:layerdigest", nil)
	rr := httptest.NewRecorder()
	router.ServeHTTP(rr, req)

	if rr.Code != http.StatusOK {
		t.Fatalf("status: got %d, want %d", rr.Code, http.StatusOK)
	}
	if ct := rr.Header().Get("Content-Type"); ct != "application/octet-stream" {
		t.Fatalf("Content-Type: got %q", ct)
	}
	if dcd := rr.Header().Get("Docker-Content-Digest"); dcd != "sha256:layerdigest" {
		t.Fatalf("Docker-Content-Digest: got %q", dcd)
	}
	if cl := rr.Header().Get("Content-Length"); cl != "19" {
		t.Fatalf("Content-Length: got %q, want %q", cl, "19")
	}
	if rr.Body.Len() != 0 {
		t.Fatalf("HEAD body should be empty, got %d bytes", rr.Body.Len())
	}
}

func TestBlobGetNotInRepo(t *testing.T) {
	fdb := newFakeDB()
	fdb.addRepo("myrepo", 1)
	// Blob NOT added to the repo.

	blobs := newFakeBlobs()
	blobs.data["sha256:orphan"] = []byte("data")

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

	req := authedRequest("GET", "/v2/myrepo/blobs/sha256:orphan", 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 != "BLOB_UNKNOWN" {
		t.Fatalf("error code: got %+v, want BLOB_UNKNOWN", body.Errors)
	}
}

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

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

	req := authedRequest("GET", "/v2/nosuchrepo/blobs/sha256:abc", 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)
	}
}

func TestBlobGetMultiSegmentRepo(t *testing.T) {
	fdb := newFakeDB()
	fdb.addRepo("org/team/app", 1)
	fdb.addBlob(1, "sha256:layerdigest")

	blobs := newFakeBlobs()
	blobs.data["sha256:layerdigest"] = []byte("data")

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

	req := authedRequest("GET", "/v2/org/team/app/blobs/sha256:layerdigest", nil)
	rr := httptest.NewRecorder()
	router.ServeHTTP(rr, req)

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