package worker_test

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"context"
	"io"
	"os"
	"path/filepath"
	"testing"

	"github.com/jfraeys/fetch_ml/internal/worker"
)

type memFetcher struct {
	calls int
	data  []byte
	err   error
}

func (m *memFetcher) Get(_ context.Context, _, _ string) (io.ReadCloser, error) {
	m.calls++
	if m.err != nil {
		return nil, m.err
	}
	return io.NopCloser(bytes.NewReader(m.data)), nil
}

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

	var buf bytes.Buffer
	gz := gzip.NewWriter(&buf)
	tw := tar.NewWriter(gz)

	for name, b := range files {
		h := &tar.Header{
			Name: name,
			Mode: 0644,
			Size: int64(len(b)),
		}
		if err := tw.WriteHeader(h); err != nil {
			t.Fatalf("tar header: %v", err)
		}
		if _, err := tw.Write(b); err != nil {
			t.Fatalf("tar write: %v", err)
		}
	}

	if err := tw.Close(); err != nil {
		t.Fatalf("tar close: %v", err)
	}
	if err := gz.Close(); err != nil {
		t.Fatalf("gz close: %v", err)
	}
	return buf.Bytes()
}

func TestResolveSnapshot_CacheHit(t *testing.T) {
	dataDir := t.TempDir()
	want := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
	cacheDir := filepath.Join(dataDir, "snapshots", "sha256", want)
	if err := os.MkdirAll(cacheDir, 0750); err != nil {
		t.Fatalf("mkdir: %v", err)
	}
	if err := os.WriteFile(filepath.Join(cacheDir, "file.txt"), []byte("ok"), 0600); err != nil {
		t.Fatalf("write: %v", err)
	}

	f := &memFetcher{err: io.EOF}
	cfg := &worker.SnapshotStoreConfig{Enabled: true, Endpoint: "minio:9000", Bucket: "b", Secure: false}
	p, err := worker.ResolveSnapshot(context.Background(), dataDir, cfg, "snap-1", want, f)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if p != cacheDir {
		t.Fatalf("unexpected path: %q", p)
	}
	if f.calls != 0 {
		t.Fatalf("expected no fetcher calls")
	}
}

func TestResolveSnapshot_DownloadAndVerify(t *testing.T) {
	dataDir := t.TempDir()

	refDir := t.TempDir()
	if err := os.WriteFile(filepath.Join(refDir, "file.txt"), []byte("ok"), 0600); err != nil {
		t.Fatalf("write: %v", err)
	}
	want, err := worker.DirOverallSHA256Hex(refDir)
	if err != nil {
		t.Fatalf("hash: %v", err)
	}

	tarBytes := makeTarGz(t, map[string][]byte{"file.txt": []byte("ok")})
	f := &memFetcher{data: tarBytes}
	cfg := &worker.SnapshotStoreConfig{Enabled: true, Endpoint: "minio:9000", Bucket: "b", Secure: false}

	p, err := worker.ResolveSnapshot(context.Background(), dataDir, cfg, "snap-1", want, f)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if f.calls != 1 {
		t.Fatalf("expected one fetch call, got %d", f.calls)
	}
	b, err := os.ReadFile(filepath.Join(p, "file.txt"))
	if err != nil {
		t.Fatalf("read: %v", err)
	}
	if string(b) != "ok" {
		t.Fatalf("unexpected contents: %q", string(b))
	}
}

func TestResolveSnapshot_ChecksumMismatch(t *testing.T) {
	dataDir := t.TempDir()
	want := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
	tarBytes := makeTarGz(t, map[string][]byte{"file.txt": []byte("ok")})
	f := &memFetcher{data: tarBytes}
	cfg := &worker.SnapshotStoreConfig{Enabled: true, Endpoint: "minio:9000", Bucket: "b", Secure: false}

	if _, err := worker.ResolveSnapshot(context.Background(), dataDir, cfg, "snap-1", want, f); err == nil {
		t.Fatalf("expected error")
	}
}

func TestResolveSnapshot_RejectsTraversal(t *testing.T) {
	dataDir := t.TempDir()
	want := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
	tarBytes := makeTarGz(t, map[string][]byte{"../evil": []byte("no")})
	f := &memFetcher{data: tarBytes}
	cfg := &worker.SnapshotStoreConfig{Enabled: true, Endpoint: "minio:9000", Bucket: "b", Secure: false}

	if _, err := worker.ResolveSnapshot(context.Background(), dataDir, cfg, "snap-1", want, f); err == nil {
		t.Fatalf("expected error")
	}
}
