package index

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/sourcegraph/zoekt"
)

var mockRepos []*zoekt.Repository

// SetTombstone idempotently sets a tombstone for repoName in .meta.
func SetTombstone(shardPath string, repoID uint32) error {
	return setTombstone(shardPath, repoID, true)
}

// UnsetTombstone idempotently removes a tombstones for reopName in .meta.
func UnsetTombstone(shardPath string, repoID uint32) error {
	return setTombstone(shardPath, repoID, false)
}

func setTombstone(shardPath string, repoID uint32, tombstone bool) error {
	var repos []*zoekt.Repository
	var err error

	if mockRepos != nil {
		repos = mockRepos
	} else {
		repos, _, err = ReadMetadataPath(shardPath)
		if err != nil {
			return err
		}
	}

	for _, repo := range repos {
		if repo.ID == repoID {
			repo.Tombstone = tombstone
		}
	}

	tempPath, finalPath, err := JsonMarshalRepoMetaTemp(shardPath, repos)
	if err != nil {
		return err
	}

	err = os.Rename(tempPath, finalPath)
	if err != nil {
		os.Remove(tempPath)
	}

	return nil
}

// JsonMarshalRepoMetaTemp writes the json encoding of the given repository metadata to a temporary file
// in the same directory as the given shard path. It returns both the path of the temporary file and the
// path of the final file that the caller should use.
//
// The caller is responsible for renaming the temporary file to the final file path, or removing
// the temporary file if it is no longer needed.
// TODO: Should we stick this in a util package?
func JsonMarshalRepoMetaTemp(shardPath string, repositoryMetadata any) (tempPath, finalPath string, err error) {
	finalPath = shardPath + ".meta"

	b, err := json.Marshal(repositoryMetadata)
	if err != nil {
		return "", "", fmt.Errorf("marshalling json: %w", err)
	}

	f, err := os.CreateTemp(filepath.Dir(finalPath), filepath.Base(finalPath)+".*.tmp")
	if err != nil {
		return "", "", fmt.Errorf("writing temporary file: %s", err)
	}

	defer func() {
		f.Close()
		if err != nil {
			_ = os.Remove(f.Name())
		}
	}()

	err = f.Chmod(0o666 &^ umask)
	if err != nil {
		return "", "", fmt.Errorf("chmoding temporary file: %s", err)
	}

	_, err = f.Write(b)
	if err != nil {
		return "", "", fmt.Errorf("writing json to temporary file: %s", err)
	}

	return f.Name(), finalPath, nil
}
