package backup

import (
	"archive/tar"
	"context"
	"fmt"
	"io"
	"os"

	"github.com/klauspost/pgzip"

	"dbbackup/internal/fs"
)

// createTarGz creates a tar.gz archive with the specified changed files
func (e *PostgresIncrementalEngine) createTarGz(ctx context.Context, outputFile string, changedFiles []ChangedFile, config *IncrementalBackupConfig) error {
	// Create output file with secure permissions (0600)
	outFile, err := fs.SecureCreate(outputFile)
	if err != nil {
		return fmt.Errorf("failed to create output file: %w", err)
	}
	defer outFile.Close()

	// Create parallel gzip writer for faster compression
	gzWriter, err := pgzip.NewWriterLevel(outFile, config.CompressionLevel)
	if err != nil {
		return fmt.Errorf("failed to create gzip writer: %w", err)
	}
	defer gzWriter.Close()

	// Create tar writer
	tarWriter := tar.NewWriter(gzWriter)
	defer tarWriter.Close()

	// Add each changed file to archive
	for i, changedFile := range changedFiles {
		// Check context cancellation
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}

		e.log.Debug("Adding file to archive",
			"file", changedFile.RelativePath,
			"progress", fmt.Sprintf("%d/%d", i+1, len(changedFiles)))

		if err := e.addFileToTar(tarWriter, changedFile); err != nil {
			return fmt.Errorf("failed to add file %s: %w", changedFile.RelativePath, err)
		}
	}

	return nil
}

// addFileToTar adds a single file to the tar archive
func (e *PostgresIncrementalEngine) addFileToTar(tarWriter *tar.Writer, changedFile ChangedFile) error {
	// Open the file
	file, err := os.Open(changedFile.AbsolutePath)
	if err != nil {
		return fmt.Errorf("failed to open file: %w", err)
	}
	defer file.Close()

	// Get file info
	info, err := file.Stat()
	if err != nil {
		return fmt.Errorf("failed to stat file: %w", err)
	}

	// Skip if file has been deleted/changed since scan
	if info.Size() != changedFile.Size {
		e.log.Warn("File size changed since scan, using current size",
			"file", changedFile.RelativePath,
			"old_size", changedFile.Size,
			"new_size", info.Size())
	}

	// Create tar header
	header := &tar.Header{
		Name:    changedFile.RelativePath,
		Size:    info.Size(),
		Mode:    int64(info.Mode()),
		ModTime: info.ModTime(),
	}

	// Write header
	if err := tarWriter.WriteHeader(header); err != nil {
		return fmt.Errorf("failed to write tar header: %w", err)
	}

	// Copy file content
	if _, err := io.Copy(tarWriter, file); err != nil {
		return fmt.Errorf("failed to copy file content: %w", err)
	}

	return nil
}
