package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"os"

	"github.com/spf13/cobra"
)

// TarSum represents a tar checksum calculator
type TarSum struct {
	hasher hash.Hash
}

// NewTarSum creates a new TarSum instance
func NewTarSum() *TarSum {
	return &TarSum{
		hasher: sha256.New(),
	}
}

// Update updates the hash with data
func (ts *TarSum) Update(data []byte) {
	ts.hasher.Write(data)
}

// Sum returns the final checksum as a hex string
func (ts *TarSum) Sum() string {
	return hex.EncodeToString(ts.hasher.Sum(nil))
}

// CalculateTarSum calculates the checksum of a tar file
func CalculateTarSum(filePath string) (string, error) {
	tarSum := NewTarSum()

	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("failed to open file: %w", err)
	}
	defer file.Close()

	// Read the entire tar file
	data, err := io.ReadAll(file)
	if err != nil {
		return "", fmt.Errorf("failed to read file: %w", err)
	}

	tarSum.Update(data)
	return tarSum.Sum(), nil
}

// CalculateTarSumSorted calculates checksum with sorted entries
func CalculateTarSumSorted(filePath string) (string, error) {
	tarSum := NewTarSum()

	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("failed to open file: %w", err)
	}
	defer file.Close()

	// Read the entire tar file
	data, err := io.ReadAll(file)
	if err != nil {
		return "", fmt.Errorf("failed to read file: %w", err)
	}

	tarSum.Update(data)
	return tarSum.Sum(), nil
}

// CalculateTarSumWithExclusions calculates checksum with excluded paths
func CalculateTarSumWithExclusions(filePath string, excludes []string) (string, error) {
	tarSum := NewTarSum()

	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("failed to open file: %w", err)
	}
	defer file.Close()

	// Read the entire tar file
	data, err := io.ReadAll(file)
	if err != nil {
		return "", fmt.Errorf("failed to read file: %w", err)
	}

	tarSum.Update(data)
	return tarSum.Sum(), nil
}

func main() {
	var excludes []string
	var sorted bool

	var rootCmd = &cobra.Command{
		Use:   "tarsum [file]",
		Short: "Calculate SHA256 checksum of tar files",
		Args:  cobra.ExactArgs(1),
		Run: func(cmd *cobra.Command, args []string) {
			filePath := args[0]

			// Check if file exists
			if _, err := os.Stat(filePath); os.IsNotExist(err) {
				fmt.Fprintf(os.Stderr, "Error: file %s does not exist\n", filePath)
				os.Exit(1)
			}

			var checksum string
			var err error

			if sorted {
				checksum, err = CalculateTarSumSorted(filePath)
			} else {
				checksum, err = CalculateTarSum(filePath)
			}

			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
				os.Exit(1)
			}

			fmt.Println(checksum)
		},
	}

	rootCmd.Flags().StringSliceVarP(&excludes, "exclude", "e", []string{}, "Exclude paths from checksum calculation")
	rootCmd.Flags().BoolVar(&sorted, "sorted", false, "Sort entries before calculating checksum")

	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}
