/*
 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
 * openFuyao is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package rdma

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestResolveHFFileSHA256PrefersLFSOID(t *testing.T) {
	file := hfModelFile{
		RFileName: "model-00001-of-00017.safetensors",
		LFS:       &hfLFSInfo{OID: "ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890"},
	}
	got := resolveHFFileSHA256(file)
	want := "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
	if got != want {
		t.Fatalf("resolveHFFileSHA256() = %q, want %q", got, want)
	}
}

func TestResolveHFFileSHA256FallsBackToBlobFilename(t *testing.T) {
	file := hfModelFile{
		RFileName: "blobs/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
	}
	got := resolveHFFileSHA256(file)
	want := "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
	if got != want {
		t.Fatalf("resolveHFFileSHA256() = %q, want %q", got, want)
	}
}

func TestResolveHFFileETagFallsBackToBlobID(t *testing.T) {
	file := hfModelFile{BlobID: "blob-123"}
	if got := resolveHFFileETag(file, ""); got != "blob-123" {
		t.Fatalf("resolveHFFileETag() = %q, want %q", got, "blob-123")
	}
}

func TestHuggingFaceEndpointAndRevisionHelpers(t *testing.T) {
	t.Parallel()

	if !IsHuggingFaceEndpoint("external", "https://huggingface.co/Qwen/Qwen3-8B") {
		t.Fatalf("expected huggingface.co endpoint to be detected")
	}
	if !IsHuggingFaceEndpoint("hf", "") {
		t.Fatalf("expected hf source type to be detected")
	}
	if IsHuggingFaceEndpoint("external", "http://example.com/model") {
		t.Fatalf("expected non-HuggingFace endpoint to be ignored")
	}
	if got := ExtractHFRevision("https://huggingface.co#rev=v1"); got != "v1" {
		t.Fatalf("ExtractHFRevision() = %q, want v1", got)
	}
	if got := ExtractHFRevision("https://huggingface.co"); got != "main" {
		t.Fatalf("ExtractHFRevision() = %q, want main", got)
	}
}

func TestResolveHuggingFaceManifestBuildsLogicalManifest(t *testing.T) {
	t.Parallel()

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/api/models/Qwen/Qwen3-8B" || r.URL.Query().Get("blobs") != "true" {
			t.Fatalf("unexpected request path: %s?%s", r.URL.Path, r.URL.RawQuery)
		}
		if got := r.Header.Get("Authorization"); got != "Bearer token-1" {
			t.Fatalf("unexpected authorization header: %q", got)
		}
		_, _ = fmt.Fprint(w, `{
			"sha":"commit-1",
			"siblings":[
				{"rfilename":"model.safetensors","size":8,"lfs":{"oid":"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"}},
				{"rfilename":"config.json","size":2,"blobId":"blob-json"},
				{"rfilename":"","size":1}
			]
		}`)
	}))
	defer server.Close()

	manifest, err := ResolveHuggingFaceManifest(context.Background(), server.Client(), HuggingFaceManifestRequest{
		Endpoint:       server.URL + "?token=token-1",
		ModelID:        "Qwen/Qwen3-8B",
		Revision:       "rev-1",
		ChunkSizeBytes: 16,
	})
	if err != nil {
		t.Fatalf("ResolveHuggingFaceManifest returned error: %v", err)
	}
	if manifest.ArtifactKey != "hf://Qwen/Qwen3-8B@rev-1" || manifest.Digest != "commit-1" {
		t.Fatalf("unexpected manifest metadata: %#v", manifest)
	}
	if len(manifest.Files) != 2 {
		t.Fatalf("expected 2 files, got %d", len(manifest.Files))
	}
	if !manifest.Files[0].Chunkable || manifest.Files[0].SHA256 == "" {
		t.Fatalf("expected safetensors file to be chunkable with sha256: %#v", manifest.Files[0])
	}
	if manifest.Files[1].ETag != "blob-json" {
		t.Fatalf("expected blob id fallback etag, got %#v", manifest.Files[1])
	}
}

func TestHuggingFaceChunkClientReadsRanges(t *testing.T) {
	t.Parallel()

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !strings.HasSuffix(r.URL.Path, "/Qwen/Qwen3-8B/resolve/main/model.safetensors") {
			t.Fatalf("unexpected request path: %s", r.URL.Path)
		}
		switch r.Method {
		case http.MethodHead:
			w.Header().Set("Content-Length", "6")
		case http.MethodGet:
			if got := r.Header.Get("Range"); got != "bytes=1-3" {
				t.Fatalf("unexpected range header: %q", got)
			}
			w.WriteHeader(http.StatusPartialContent)
			_, _ = w.Write([]byte("abc"))
		default:
			t.Fatalf("unexpected method: %s", r.Method)
		}
	}))
	defer server.Close()

	client := NewHuggingFaceChunkClientWithToken(server.Client(), "")
	size, err := client.Stat(context.Background(), server.URL, "Qwen/Qwen3-8B", "model.safetensors")
	if err != nil {
		t.Fatalf("Stat returned error: %v", err)
	}
	if size != 6 {
		t.Fatalf("expected size 6, got %d", size)
	}
	payload, err := client.ReadAt(context.Background(), server.URL, "Qwen/Qwen3-8B", "model.safetensors", 1, 3)
	if err != nil {
		t.Fatalf("ReadAt returned error: %v", err)
	}
	if string(payload) != "abc" {
		t.Fatalf("unexpected payload: %q", string(payload))
	}
}

func TestResolveHuggingFaceManifestReportsAPIAndDecodeErrors(t *testing.T) {
	t.Parallel()

	errorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		http.Error(w, "nope", http.StatusTeapot)
	}))
	defer errorServer.Close()

	if _, err := ResolveHuggingFaceManifest(context.Background(), errorServer.Client(), HuggingFaceManifestRequest{
		Endpoint: errorServer.URL,
		ModelID:  "Qwen/Qwen3-8B",
	}); err == nil || !strings.Contains(err.Error(), "status 418") {
		t.Fatalf("expected API status error, got %v", err)
	}

	badJSONServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte("{"))
	}))
	defer badJSONServer.Close()

	if _, err := ResolveHuggingFaceManifest(context.Background(), badJSONServer.Client(), HuggingFaceManifestRequest{
		Endpoint: badJSONServer.URL,
		ModelID:  "Qwen/Qwen3-8B",
	}); err == nil || !strings.Contains(err.Error(), "decode hf api response") {
		t.Fatalf("expected decode error, got %v", err)
	}
}

func TestHuggingFaceManifestDefaultsAndEndpointNormalization(t *testing.T) {
	t.Parallel()

	endpoint := normalizeHuggingFaceManifestEndpoint("https://huggingface.co?token=abc#rev=v1", "")
	if endpoint.endpoint != "https://huggingface.co" || endpoint.token != "abc" {
		t.Fatalf("unexpected normalized endpoint: %#v", endpoint)
	}
	if got := defaultHuggingFaceRevision(""); got != "main" {
		t.Fatalf("expected default revision main, got %q", got)
	}
	if got := normalizeHuggingFaceManifestChunkSize(0); got != defaultHuggingFaceManifestChunkSizeBytes {
		t.Fatalf("expected default chunk size, got %d", got)
	}
	if got := huggingFaceManifestAPIURL("https://huggingface.co/", "Qwen/Qwen3-8B"); got != "https://huggingface.co/api/models/Qwen/Qwen3-8B?blobs=true" {
		t.Fatalf("unexpected API URL: %s", got)
	}
}