package releases

import (
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"ymhut-box/server/unified-management/internal/config"
	"ymhut-box/server/unified-management/internal/db"
)

func TestCompareVersion(t *testing.T) {
	cases := []struct {
		a    string
		b    string
		want int
	}{
		{"2.0.6.31", "2.0.6.2", 1},
		{"2.0.10", "2.0.9", 1},
		{"2.0.6.2", "2.0.6.31", -1},
		{"2.0.6", "2.0.6.0", 0},
	}
	for _, tc := range cases {
		if got := compareVersion(tc.a, tc.b); got != tc.want {
			t.Fatalf("compareVersion(%q, %q) = %d, want %d", tc.a, tc.b, got, tc.want)
		}
	}
}

func TestDetectPackageMetadata(t *testing.T) {
	platform, arch := detectPlatform("YMhutBox_2.0.6.31_x64.msix")
	if platform != "windows" || arch != "x64" {
		t.Fatalf("detectPlatform returned %s/%s", platform, arch)
	}
	if version := detectVersion("YMhut_Box_WinUI_Setup_2.0.6.31.exe"); version != "2.0.6.31" {
		t.Fatalf("detectVersion returned %q", version)
	}
}

func TestSaveUploadedPackageWritesFileAndUpdatesManifest(t *testing.T) {
	dir := t.TempDir()
	cfg := &config.Config{
		BaseDir:         dir,
		StorageDir:      filepath.Join(dir, "storage"),
		DataDir:         filepath.Join(dir, "data"),
		UpdatePublicDir: filepath.Join(dir, "data", "update", "public"),
		DownloadsDir:    filepath.Join(dir, "data", "update", "public", "downloads"),
		BaseURL:         "https://update.ymhut.cn",
		Database: config.DatabaseConfig{
			Provider:          "sqlite",
			SQLitePath:        filepath.Join(dir, "storage", "unified.sqlite"),
			HealthIntervalSec: 30,
		},
	}
	store, err := db.Open(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer store.Close()
	service := NewService(cfg, store)
	req := httptest.NewRequest("POST", "https://update.ymhut.cn/api/admin/releases/packages", nil)
	pkg, err := service.SaveUploadedPackage(req, strings.NewReader("package bytes"), UploadOptions{
		FileName:       "YMhut_Box_WinUI_Setup_2.0.6.31.exe",
		UpdateManifest: true,
	}, "admin")
	if err != nil {
		t.Fatal(err)
	}
	if pkg.Version != "2.0.6.31" || pkg.SHA256 == "" || pkg.Size == 0 {
		t.Fatalf("unexpected package metadata: %#v", pkg)
	}
	if _, err := os.Stat(filepath.Join(cfg.DownloadsDir, pkg.FileName)); err != nil {
		t.Fatal(err)
	}
	manifest := readJSON(filepath.Join(cfg.UpdatePublicDir, "update-info.json"))
	if manifest["download_url"] != pkg.URL || manifest["package_sha256"] != pkg.SHA256 {
		t.Fatalf("manifest not updated: %#v", manifest)
	}
}

func TestSaveUploadedPackageRejectsUnsafeName(t *testing.T) {
	dir := t.TempDir()
	cfg := &config.Config{
		BaseDir:         dir,
		StorageDir:      filepath.Join(dir, "storage"),
		DataDir:         filepath.Join(dir, "data"),
		UpdatePublicDir: filepath.Join(dir, "data", "update", "public"),
		DownloadsDir:    filepath.Join(dir, "data", "update", "public", "downloads"),
		Database: config.DatabaseConfig{
			Provider:          "sqlite",
			SQLitePath:        filepath.Join(dir, "storage", "unified.sqlite"),
			HealthIntervalSec: 30,
		},
	}
	store, err := db.Open(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer store.Close()
	service := NewService(cfg, store)
	_, err = service.SaveUploadedPackage(httptest.NewRequest("POST", "/", nil), strings.NewReader("x"), UploadOptions{FileName: "../evil.exe"}, "admin")
	if err == nil {
		t.Fatal("expected unsafe filename to be rejected")
	}
}
