package gitutil

import (
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

// GitState contains information about the Git repository state.
type GitState struct {
	// Date is the current commit datetime.
	Date time.Time
	// IsDirty indicates whether there are uncommitted changes.
	IsDirty bool
	// IsRepo indicates whether the directory is a Git repository.
	IsRepo bool
	// SHA is the full SHA-1 (40 characters) of the current commit.
	SHA string
}

// InspectGitState inspects the Git repo state from the specified directory.
// If the directory is not a Git repository, or if git utility is not available,
// returns GitState with IsRepo=false and no error.
func InspectGitState(dir string) (GitState, error) {
	// Check if git command is available.
	if _, err := exec.LookPath("git"); err != nil {
		return GitState{}, nil
	}

	state := GitState{
		IsRepo: isGitRepo(dir),
	}
	if !state.IsRepo {
		return state, nil
	}

	// Get the current commit SHA. An initialised but empty repo has no HEAD yet,
	// so treat it as a non-repo so callers can fall back to non-git logic.
	sha, err := gitCommand(dir, "rev-parse", "--verify", "HEAD")
	if err != nil {
		state.IsRepo = false
		return state, nil
	}
	state.SHA = strings.TrimSpace(sha)

	// Get the current commit timestamp.
	timestamp, err := gitCommand(dir, "log", "-1", "--format=%ct")
	if err != nil {
		return state, fmt.Errorf("get current commit timestamp: %w", err)
	}
	seconds, err := strconv.ParseInt(strings.TrimSpace(timestamp), 10, 64)
	if err != nil {
		return state, fmt.Errorf("parse current commit timestamp: %w", err)
	}
	state.Date = time.Unix(seconds, 0).UTC()

	// Check for uncommitted changes.
	status, err := gitCommand(dir, "status", "--porcelain")
	if err != nil {
		return state, fmt.Errorf("check git status: %w", err)
	}
	state.IsDirty = strings.TrimSpace(status) != ""

	return state, nil
}

// isGitRepo checks if the directory is a Git repository.
func isGitRepo(dir string) bool {
	_, err := gitCommand(dir, "rev-parse", "--git-dir")
	return err == nil
}

// gitCommand runs a git command in the specified directory.
func gitCommand(dir string, args ...string) (string, error) {
	cmd := exec.Command("git", args...)
	cmd.Dir = dir
	output, err := cmd.Output()
	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			return "", fmt.Errorf("git command failed: %s", exitErr.Stderr)
		}
		return "", err
	}

	return string(output), nil
}

// ShortSHA returns a truncated SHA with the specified length.
// If length is negative, zero, or greater than the SHA length, it returns the full SHA.
// Returns empty string if SHA is empty.
func (s *GitState) ShortSHA(length int) string {
	if length <= 0 || length > len(s.SHA) {
		length = len(s.SHA)
	}
	return s.SHA[:length]
}
