/*
 * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 */
package auxutar

import (
	"archive/tar"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func Archive(srcdir, dstpath string) error {
	var err error
	srcdir = filepath.Clean(srcdir)
	dstpath = filepath.Clean(dstpath)

	err = os.MkdirAll(filepath.Dir(dstpath), 0750)
	if err != nil {
		return err
	}
	tarFile, err := os.OpenFile(dstpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
	if err != nil {
		return err
	}
	defer tarFile.Close()

	tarWriter := tar.NewWriter(tarFile)
	defer tarWriter.Close()

	walker := func(filename string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !fileInfo.Mode().IsRegular() {
			return nil
		}
		header, err := tar.FileInfoHeader(fileInfo, fileInfo.Name())
		if err != nil {
			return err
		}
		header.Name = strings.TrimPrefix(filename, filepath.Clean(srcdir))
		header.Name = strings.TrimPrefix(header.Name, string(filepath.Separator))
		err = tarWriter.WriteHeader(header)
		if err != nil {
			return err
		}
		wrapfunc := func() error {
			file, err := os.Open(filename)
			if err != nil {
				return err
			}
			defer file.Close()
			_, err = io.Copy(tarWriter, file)
			if err != nil {
				return err
			}
			return err
		}
		err = wrapfunc()
		if err != nil {
			return err
		}
		return nil
	}
	err = filepath.Walk(srcdir, walker)
	if err != nil {
		return err
	}
	return err
}

func Unarchive(filename, dstdir string) error {
	var err error
	err = os.MkdirAll(dstdir, 0755)
	if err != nil {
		return err
	}
	file, err := os.OpenFile(filename, os.O_RDONLY, 0)
	if err != nil {
		return err
	}
	defer file.Close()

	tarReader := tar.NewReader(file)
	for {
		header, err := tarReader.Next()
		switch {
		case err == io.EOF:
			return nil
		case err != nil:
			return err
		case header == nil:
			continue
		}
		target := filepath.Join(dstdir, header.Name)
		target = filepath.Clean(target)

		fileInfo := header.FileInfo()
		switch header.Typeflag {
		case tar.TypeDir:
			_, err := os.Stat(target)
			if os.IsNotExist(err) {
				err = nil
				err := os.MkdirAll(target, fileInfo.Mode())
				if err != nil {
					return err
				}
			}
			if err != nil {
				return err
			}
		case tar.TypeReg:
			wrapfunc := func() error {
				dir := filepath.Dir(target)
				_, err := os.Stat(dir)
				if os.IsNotExist(err) {
					err := os.MkdirAll(dir, 0750)
					if err != nil {
						return err
					}
				}
				file, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, fileInfo.Mode())
				if err != nil {
					return err
				}
				defer file.Close()
				_, err = io.Copy(file, tarReader)
				if err != nil {
					return err
				}
				return err
			}
			err = wrapfunc()
			if err != nil {
				return err
			}
		}
		err = os.Chtimes(target, time.Now(), fileInfo.ModTime())
		if err != nil {
			return err
		}
		err = file.Chmod(fileInfo.Mode())
		if err != nil {
			return err
		}
	}
	return err
}
