package imagepull

import (
	"context"
	"errors"
	"fmt"
	"os"

	"banger/internal/system"
)

// MinExt4Size is the smallest ext4 image we'll create. mkfs.ext4 needs a
// few megabytes for its bookkeeping; for a real rootfs the staging tree
// will dominate anyway.
const MinExt4Size int64 = 1 << 20 * 64 // 64 MiB

// BuildExt4 creates outFile as a sparse ext4 image of sizeBytes and
// populates it from srcDir using `mkfs.ext4 -F -d`. No mount, no sudo.
//
// sizeBytes must be at least MinExt4Size. Callers size the file with
// headroom over the staged tree (the daemon orchestrator does this;
// this function only enforces a sanity floor).
//
// The filesystem itself is root-owned via `-E root_owner=0:0`, but
// the per-file uid/gid/mode inside srcDir are the runner's — Go's
// unprivileged tar extraction can't preserve them. The pipeline's
// next step, ApplyOwnership, restores the tar-header values.
func BuildExt4(ctx context.Context, runner system.CommandRunner, srcDir, outFile string, sizeBytes int64) error {
	if sizeBytes < MinExt4Size {
		return fmt.Errorf("ext4 size %d below minimum %d", sizeBytes, MinExt4Size)
	}
	info, err := os.Stat(srcDir)
	if err != nil {
		return fmt.Errorf("stat source: %w", err)
	}
	if !info.IsDir() {
		return fmt.Errorf("%s is not a directory", srcDir)
	}

	if err := os.Remove(outFile); err != nil && !errors.Is(err, os.ErrNotExist) {
		return err
	}
	f, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0o644)
	if err != nil {
		return err
	}
	if err := f.Truncate(sizeBytes); err != nil {
		_ = f.Close()
		_ = os.Remove(outFile)
		return err
	}
	if err := f.Close(); err != nil {
		_ = os.Remove(outFile)
		return err
	}

	// mkfs.ext4's positional `fs-size` is documented in 1 KiB units
	// (NOT the filesystem's 4 KiB block size), so dividing by 4096
	// produces a filesystem 1/4 the intended size. Omit the positional
	// entirely — the file was truncated to sizeBytes above, and mkfs
	// with no fs-size arg uses the whole device.
	out, runErr := runner.Run(ctx, "mkfs.ext4",
		"-F",
		"-q",
		"-d", srcDir,
		"-L", "banger-rootfs",
		"-E", system.MkfsExtraOptions,
		outFile,
	)
	if runErr != nil {
		_ = os.Remove(outFile)
		return fmt.Errorf("mkfs.ext4 -d: %w: %s", runErr, string(out))
	}
	return nil
}
