package pack

import (
	"archive/zip"
	"bufio"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestLoadPathSupportsDirectory(t *testing.T) {
	loaded, err := LoadPath(filepath.Join("..", "..", "packs", "openai-cn-pack"))
	if err != nil {
		t.Fatalf("LoadPath(dir) error = %v", err)
	}
	if loaded.Manifest.PackID != "openai-cn-pack" {
		t.Fatalf("PackID = %q, want %q", loaded.Manifest.PackID, "openai-cn-pack")
	}
}

func TestLoadPathIncludesFirstBatchOfficialProviders(t *testing.T) {
	loaded, err := LoadPath(filepath.Join("..", "..", "packs", "openai-cn-pack"))
	if err != nil {
		t.Fatalf("LoadPath(dir) error = %v", err)
	}

	got := make(map[string]struct{}, len(loaded.Providers))
	for _, provider := range loaded.Providers {
		got[provider.ProviderID] = struct{}{}
	}

	want := []string{
		"qwen-official",
		"qwen-coder-official",
		"deepseek-chat-official",
		"deepseek-reasoner-official",
		"glm-5-1-official",
		"glm-4-7-official",
		"kimi-k2-5-official",
		"kimi-k2-thinking-official",
		"minimax-m2-7-official",
		"step-3-5-flash-official",
	}
	for _, providerID := range want {
		if _, ok := got[providerID]; !ok {
			t.Fatalf("Providers missing %q; got %v", providerID, providerIDs(loaded.Providers))
		}
	}
}

func TestLoadPathSupportsShadowASXSPack(t *testing.T) {
	loaded, err := LoadPath(filepath.Join("..", "..", "packs", "openai-cn-pack-shadow-asxs"))
	if err != nil {
		t.Fatalf("LoadPath(shadow-asxs-pack) error = %v", err)
	}
	if loaded.Manifest.PackID != "openai-cn-pack-shadow-asxs" {
		t.Fatalf("PackID = %q, want %q", loaded.Manifest.PackID, "openai-cn-pack-shadow-asxs")
	}
	if len(loaded.Providers) != 1 {
		t.Fatalf("Providers len = %d, want 1", len(loaded.Providers))
	}
	provider := loaded.Providers[0]
	if provider.ProviderID != "gpt-asxs-shadow-lab" {
		t.Fatalf("ProviderID = %q, want %q", provider.ProviderID, "gpt-asxs-shadow-lab")
	}
	if len(provider.DefaultModels) != 2 || provider.DefaultModels[0] != "gpt-5.4" {
		t.Fatalf("DefaultModels = %v, want canonical gpt-5.4 models", provider.DefaultModels)
	}
}

func TestLoadPathSupportsZipArchive(t *testing.T) {
	tempDir := t.TempDir()
	archivePath := filepath.Join(tempDir, "openai-cn-pack.zip")
	writePackArchive(t, archivePath)

	loaded, err := LoadPath(archivePath)
	if err != nil {
		t.Fatalf("LoadPath(zip) error = %v", err)
	}
	if loaded.Manifest.PackID != "openai-cn-pack" {
		t.Fatalf("PackID = %q, want %q", loaded.Manifest.PackID, "openai-cn-pack")
	}
	if len(loaded.Providers) == 0 {
		t.Fatal("Providers = 0, want parsed providers from archive")
	}
}

func writePackArchive(t *testing.T, archivePath string) {
	t.Helper()
	file, err := os.Create(archivePath)
	if err != nil {
		t.Fatalf("os.Create() error = %v", err)
	}
	defer file.Close()

	writer := zip.NewWriter(file)
	defer writer.Close()

	sourceRoot := filepath.Join("..", "..", "packs", "openai-cn-pack")
	files := []string{"pack.json", "checksums.txt"}
	checksumFile, err := os.Open(filepath.Join(sourceRoot, "checksums.txt"))
	if err != nil {
		t.Fatalf("os.Open(checksums.txt) error = %v", err)
	}
	defer checksumFile.Close()
	scanner := bufio.NewScanner(checksumFile)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" {
			continue
		}
		parts := strings.Fields(line)
		if len(parts) != 2 {
			t.Fatalf("invalid checksum line %q", line)
		}
		files = append(files, filepath.FromSlash(parts[1]))
	}
	if err := scanner.Err(); err != nil {
		t.Fatalf("scan checksums.txt error = %v", err)
	}
	for _, relativePath := range files {
		body, err := os.ReadFile(filepath.Join(sourceRoot, relativePath))
		if err != nil {
			t.Fatalf("os.ReadFile(%q) error = %v", relativePath, err)
		}
		entry, err := writer.Create(filepath.ToSlash(filepath.Join("openai-cn-pack", relativePath)))
		if err != nil {
			t.Fatalf("Create(%q) error = %v", relativePath, err)
		}
		if _, err := entry.Write(body); err != nil {
			t.Fatalf("Write(%q) error = %v", relativePath, err)
		}
	}
	if err := writer.Close(); err != nil {
		t.Fatalf("Close archive writer: %v", err)
	}
}

func providerIDs(providers []ProviderManifest) []string {
	ids := make([]string, 0, len(providers))
	for _, provider := range providers {
		ids = append(ids, provider.ProviderID)
	}
	return ids
}
