package manifest

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

// Manifest holds database metadata used for recovery.
// The MANIFEST file stores the recovery checkpoint so that
// recovery knows which segments are already confirmed durable.
type Manifest struct {
	RecoverySegmentID uint64
}

// Load reads the MANIFEST file from dir.
// If the file does not exist, it returns a zero-value Manifest with no error (fresh DB).
func Load(dir string) (*Manifest, error) {
	path := dir + "/MANIFEST"
	data, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return &Manifest{RecoverySegmentID: 0}, nil
		}
		return nil, fmt.Errorf("read manifest: %w", err)
	}

	line := strings.TrimSpace(string(data))
	if !strings.HasPrefix(line, "recovery_segment_id:") {
		return nil, fmt.Errorf("manifest: invalid format: %q", line)
	}
	idStr := strings.TrimPrefix(line, "recovery_segment_id:")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("manifest: parse recovery_segment_id: %w", err)
	}
	return &Manifest{RecoverySegmentID: id}, nil
}

// Save atomically writes the MANIFEST file to dir with the given recoverySegmentID.
func Save(dir string, recoverySegmentID uint64) error {
	content := fmt.Sprintf("recovery_segment_id:%d\n", recoverySegmentID)
	tmpPath := dir + "/MANIFEST.tmp"
	if err := os.WriteFile(tmpPath, []byte(content), 0o644); err != nil {
		return fmt.Errorf("write manifest tmp: %w", err)
	}
	if err := os.Rename(tmpPath, dir+"/MANIFEST"); err != nil {
		return fmt.Errorf("rename manifest: %w", err)
	}
	return nil
}
