package pack

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

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

	if _, err := exec.LookPath("git"); err != nil {
		t.Skip("git is required for publish test")
	}

	repoRoot := t.TempDir()
	packDir := filepath.Join(repoRoot, "packs", "openai-cn-pack")
	createPackFixtureAt(t, packDir, map[string]string{
		"pack.json": `{
  "pack_id": "openai-cn-pack",
  "version": "1.1.4",
  "vendor": "YourTeam",
  "target_host": "sub2api",
  "min_host_version": "0.1.126",
  "max_host_version": "0.2.x",
  "providers_dir": "providers",
  "checksum_file": "checksums.txt"
}`,
		"providers/minimax-53hk.json": `{
  "provider_id": "minimax-53hk",
  "display_name": "MiniMax 53hk 中转兼容",
  "base_url": "https://api.53hk.cn/v1",
  "platform": "openai",
  "account_type": "apikey",
  "default_models": ["MiniMax-M2.7-highspeed"],
  "smoke_test_model": "MiniMax-M2.7-highspeed",
  "group_template": {"name": "g", "rate_multiplier": 1.0},
  "channel_template": {"name": "c", "model_mapping": {"MiniMax-M2.7-highspeed": "MiniMax-M2.7-highspeed"}},
  "plan_template": {"name": "p", "price": 1, "validity_days": 30, "validity_unit": "day"},
  "import": {"supports_multi_key": true, "supports_strict": true, "supports_partial": true}
}`,
	})
	initGitRepo(t, repoRoot)

	result, err := PublishProviderManifest(context.Background(), PublishProviderManifestRequest{
		RepoRoot: repoRoot,
		PackID:   "openai-cn-pack",
		Manifest: ProviderManifest{
			ProviderID:     "openai-zhongzhuan",
			DisplayName:    "OpenAI 中转",
			BaseURL:        "https://api.example.com/v1",
			Platform:       "openai",
			SmokeTestModel: "gpt-5.4",
			DefaultModels:  []string{"gpt-5.4", "gpt-5.4-mini"},
		},
		CommitMessage: "feat(pack): publish openai-zhongzhuan",
	})
	if err != nil {
		t.Fatalf("PublishProviderManifest() error = %v", err)
	}

	if result.PublishMode != "created" {
		t.Fatalf("PublishMode = %q, want created", result.PublishMode)
	}
	if result.PackVersionBefore != "1.1.4" || result.PackVersionAfter != "1.1.5" {
		t.Fatalf("pack versions = %q -> %q, want 1.1.4 -> 1.1.5", result.PackVersionBefore, result.PackVersionAfter)
	}
	if strings.TrimSpace(result.CommitSHA) == "" {
		t.Fatal("CommitSHA is empty")
	}

	publishedPack, err := LoadDir(packDir)
	if err != nil {
		t.Fatalf("LoadDir() after publish error = %v", err)
	}
	if publishedPack.Manifest.Version != "1.1.5" {
		t.Fatalf("published pack version = %q, want 1.1.5", publishedPack.Manifest.Version)
	}

	body, err := readFileString(filepath.Join(packDir, "providers", "openai-zhongzhuan.json"))
	if err != nil {
		t.Fatalf("readFileString() provider error = %v", err)
	}
	var provider ProviderManifest
	if err := json.Unmarshal([]byte(body), &provider); err != nil {
		t.Fatalf("json.Unmarshal() provider error = %v", err)
	}
	if provider.AccountType != "apikey" {
		t.Fatalf("AccountType = %q, want apikey", provider.AccountType)
	}
	if provider.ChannelTemplate.ModelMapping["gpt-5.4-mini"] != "gpt-5.4-mini" {
		t.Fatalf("ChannelTemplate.ModelMapping = %+v, want identity mapping for gpt-5.4-mini", provider.ChannelTemplate.ModelMapping)
	}

	checksums, err := readFileString(filepath.Join(packDir, "checksums.txt"))
	if err != nil {
		t.Fatalf("readFileString() checksums error = %v", err)
	}
	if !strings.Contains(checksums, "providers/openai-zhongzhuan.json") {
		t.Fatalf("checksums.txt = %q, want providers/openai-zhongzhuan.json entry", checksums)
	}
}

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

	_, err := PublishProviderManifest(context.Background(), PublishProviderManifestRequest{
		PackID: "openai-cn-pack",
		Manifest: ProviderManifest{
			ProviderID:     "deepseek",
			DisplayName:    "DeepSeek",
			BaseURL:        "https://api.deepseek.com",
			Platform:       "openai",
			SmokeTestModel: "deepseek-chat",
			DefaultModels:  []string{"deepseek-chat"},
		},
	})
	if err == nil || !strings.Contains(err.Error(), "repo root") {
		t.Fatalf("PublishProviderManifest() error = %v, want repo root detail", err)
	}
}

func createPackFixtureAt(t *testing.T, packDir string, files map[string]string) {
	t.Helper()

	var lines []string
	for relativePath, content := range files {
		absolutePath := filepath.Join(packDir, relativePath)
		mustWrite(t, absolutePath, content)
		sum := sha256Sum(content)
		lines = append(lines, sum+"  "+relativePath)
	}
	mustWrite(t, filepath.Join(packDir, "checksums.txt"), strings.Join(lines, "\n")+"\n")
}

func sha256Sum(content string) string {
	sum := sha256.Sum256([]byte(content))
	return hex.EncodeToString(sum[:])
}

func initGitRepo(t *testing.T, repoRoot string) {
	t.Helper()
	runGitTest(t, repoRoot, "init")
	runGitTest(t, repoRoot, "config", "user.name", "Test User")
	runGitTest(t, repoRoot, "config", "user.email", "test@example.com")
	runGitTest(t, repoRoot, "add", ".")
	runGitTest(t, repoRoot, "commit", "-m", "chore: seed pack fixture")
}

func runGitTest(t *testing.T, repoRoot string, args ...string) {
	t.Helper()
	cmd := exec.Command("git", append([]string{"-C", repoRoot}, args...)...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("git %v error = %v: %s", args, err, strings.TrimSpace(string(output)))
	}
}

func readFileString(path string) (string, error) {
	body, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(body), nil
}
