package archive

import (
	"bytes"
	"os"
	"path/filepath"
	"testing"
)

func TestCreateArchive(t *testing.T) {
	// Create a temporary directory for tests
	tempDir, err := os.MkdirTemp("", "agate-test-*")
	if err != nil {
		t.Fatalf("Failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(tempDir) // Clean up after test

	// Create a source directory with some files
	sourceDir := filepath.Join(tempDir, "source")
	if err := os.MkdirAll(sourceDir, 0755); err != nil {
		t.Fatalf("Failed to create source directory: %v", err)
	}

	// Create a subdirectory
	subDir := filepath.Join(sourceDir, "subdir")
	if err := os.MkdirAll(subDir, 0755); err != nil {
		t.Fatalf("Failed to create subdirectory: %v", err)
	}

	// Create some test files
	testFiles := map[string]string{
		filepath.Join(sourceDir, "file1.txt"): "This is file 1",
		filepath.Join(sourceDir, "file2.txt"): "This is file 2",
		filepath.Join(subDir, "subfile1.txt"): "This is subfile 1",
		filepath.Join(subDir, "subfile2.txt"): "This is subfile 2",
	}

	for path, content := range testFiles {
		if err := os.WriteFile(path, []byte(content), 0644); err != nil {
			t.Fatalf("Failed to create test file %s: %v", path, err)
		}
	}

	// Create the archive
	archivePath := filepath.Join(tempDir, "archive.zip")
	err = CreateArchive(sourceDir, archivePath)
	if err != nil {
		t.Fatalf("Failed to create archive: %v", err)
	}

	// Check that the archive file was created
	if _, err := os.Stat(archivePath); os.IsNotExist(err) {
		t.Fatalf("Archive file was not created")
	}

	// Test creating archive with non-existent source directory
	err = CreateArchive(filepath.Join(tempDir, "nonexistent"), archivePath)
	if err == nil {
		t.Fatalf("Expected error when creating archive from non-existent directory, got nil")
	}

	// Test creating archive with a file as source
	fileSourcePath := filepath.Join(tempDir, "file_source.txt")
	if err := os.WriteFile(fileSourcePath, []byte("This is a file"), 0644); err != nil {
		t.Fatalf("Failed to create test file: %v", err)
	}
	err = CreateArchive(fileSourcePath, archivePath)
	if err == nil {
		t.Fatalf("Expected error when creating archive from a file, got nil")
	}
}

func TestListArchiveContents(t *testing.T) {
	// Create a temporary directory for tests
	tempDir, err := os.MkdirTemp("", "agate-test-*")
	if err != nil {
		t.Fatalf("Failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(tempDir) // Clean up after test

	// Create a source directory with some files
	sourceDir := filepath.Join(tempDir, "source")
	if err := os.MkdirAll(sourceDir, 0755); err != nil {
		t.Fatalf("Failed to create source directory: %v", err)
	}

	// Create a subdirectory
	subDir := filepath.Join(sourceDir, "subdir")
	if err := os.MkdirAll(subDir, 0755); err != nil {
		t.Fatalf("Failed to create subdirectory: %v", err)
	}

	// Create some test files
	testFiles := map[string]string{
		filepath.Join(sourceDir, "file1.txt"): "This is file 1",
		filepath.Join(sourceDir, "file2.txt"): "This is file 2",
		filepath.Join(subDir, "subfile1.txt"): "This is subfile 1",
		filepath.Join(subDir, "subfile2.txt"): "This is subfile 2",
	}

	for path, content := range testFiles {
		if err := os.WriteFile(path, []byte(content), 0644); err != nil {
			t.Fatalf("Failed to create test file %s: %v", path, err)
		}
	}

	// Create the archive
	archivePath := filepath.Join(tempDir, "archive.zip")
	err = CreateArchive(sourceDir, archivePath)
	if err != nil {
		t.Fatalf("Failed to create archive: %v", err)
	}

	// List the archive contents
	entries, err := ListArchiveContents(archivePath)
	if err != nil {
		t.Fatalf("Failed to list archive contents: %v", err)
	}

	// Check that all files and directories are listed
	expectedEntries := map[string]bool{
		"file1.txt":           false,
		"file2.txt":           false,
		"subdir/":             true,
		"subdir/subfile1.txt": false,
		"subdir/subfile2.txt": false,
	}

	if len(entries) != len(expectedEntries) {
		t.Errorf("Wrong number of entries: got %d, want %d", len(entries), len(expectedEntries))
	}

	for _, entry := range entries {
		isDir, exists := expectedEntries[entry.Path]
		if !exists {
			t.Errorf("Unexpected entry in archive: %s", entry.Path)
			continue
		}
		if entry.IsDir != isDir {
			t.Errorf("Entry %s has wrong IsDir value: got %v, want %v", entry.Path, entry.IsDir, isDir)
		}
	}

	// Test listing contents of non-existent archive
	_, err = ListArchiveContents(filepath.Join(tempDir, "nonexistent.zip"))
	if err == nil {
		t.Fatalf("Expected error when listing contents of non-existent archive, got nil")
	}
}

func TestExtractFileFromArchive(t *testing.T) {
	// Create a temporary directory for tests
	tempDir, err := os.MkdirTemp("", "agate-test-*")
	if err != nil {
		t.Fatalf("Failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(tempDir) // Clean up after test

	// Create a source directory with some files
	sourceDir := filepath.Join(tempDir, "source")
	if err := os.MkdirAll(sourceDir, 0755); err != nil {
		t.Fatalf("Failed to create source directory: %v", err)
	}

	// Create a subdirectory
	subDir := filepath.Join(sourceDir, "subdir")
	if err := os.MkdirAll(subDir, 0755); err != nil {
		t.Fatalf("Failed to create subdirectory: %v", err)
	}

	// Create some test files
	testFiles := map[string]string{
		filepath.Join(sourceDir, "file1.txt"): "This is file 1",
		filepath.Join(sourceDir, "file2.txt"): "This is file 2",
		filepath.Join(subDir, "subfile1.txt"): "This is subfile 1",
		filepath.Join(subDir, "subfile2.txt"): "This is subfile 2",
	}

	for path, content := range testFiles {
		if err := os.WriteFile(path, []byte(content), 0644); err != nil {
			t.Fatalf("Failed to create test file %s: %v", path, err)
		}
	}

	// Create the archive
	archivePath := filepath.Join(tempDir, "archive.zip")
	err = CreateArchive(sourceDir, archivePath)
	if err != nil {
		t.Fatalf("Failed to create archive: %v", err)
	}

	// Extract a file from the archive
	var buf bytes.Buffer
	err = ExtractFileFromArchive(archivePath, "file1.txt", &buf)
	if err != nil {
		t.Fatalf("Failed to extract file from archive: %v", err)
	}

	// Check that the extracted content matches the original
	if buf.String() != "This is file 1" {
		t.Errorf("Extracted content does not match: got %s, want %s", buf.String(), "This is file 1")
	}

	// Extract a file from a subdirectory
	buf.Reset()
	err = ExtractFileFromArchive(archivePath, "subdir/subfile1.txt", &buf)
	if err != nil {
		t.Fatalf("Failed to extract file from archive: %v", err)
	}

	// Check that the extracted content matches the original
	if buf.String() != "This is subfile 1" {
		t.Errorf("Extracted content does not match: got %s, want %s", buf.String(), "This is subfile 1")
	}

	// Try to extract a non-existent file
	buf.Reset()
	err = ExtractFileFromArchive(archivePath, "nonexistent.txt", &buf)
	if err != ErrFileNotFoundInArchive {
		t.Fatalf("Expected ErrFileNotFoundInArchive when extracting non-existent file, got: %v", err)
	}

	// Try to extract a directory
	buf.Reset()
	err = ExtractFileFromArchive(archivePath, "subdir/", &buf)
	if err == nil {
		t.Fatalf("Expected error when extracting a directory, got nil")
	}

	// Try to extract from a non-existent archive
	buf.Reset()
	err = ExtractFileFromArchive(filepath.Join(tempDir, "nonexistent.zip"), "file1.txt", &buf)
	if err == nil {
		t.Fatalf("Expected error when extracting from non-existent archive, got nil")
	}
}