package git

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

// DetectRepo attempts to detect the repository owner/name from the current git directory.
// It reads .git/config and parses the origin remote URL.
func DetectRepo() (owner, name string, err error) {
	// Find .git/config file
	gitConfigPath, err := findGitConfig()
	if err != nil {
		return "", "", err
	}

	// Parse .git/config
	remoteURL, err := parseGitConfig(gitConfigPath)
	if err != nil {
		return "", "", err
	}

	// Extract owner/name from URL
	owner, name, _, err = parseRemoteURL(remoteURL)
	return owner, name, err
}

// DetectHost attempts to detect the Forgejo instance hostname from the current git directory.
// It reads .git/config and parses the origin remote URL to extract the hostname.
func DetectHost() (hostname string, err error) {
	// Find .git/config file
	gitConfigPath, err := findGitConfig()
	if err != nil {
		return "", err
	}

	// Parse .git/config
	remoteURL, err := parseGitConfig(gitConfigPath)
	if err != nil {
		return "", err
	}

	// Extract hostname from URL
	_, _, hostname, err = parseRemoteURL(remoteURL)
	return hostname, err
}

// findGitConfig searches for .git/config starting from the current directory
func findGitConfig() (string, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return "", fmt.Errorf("failed to get current directory: %w", err)
	}

	dir := cwd
	for {
		gitConfigPath := filepath.Join(dir, ".git", "config")
		if _, err := os.Stat(gitConfigPath); err == nil {
			return gitConfigPath, nil
		}

		parent := filepath.Dir(dir)
		if parent == dir {
			// Reached root directory
			return "", fmt.Errorf("not in a git repository (or any parent up to mount point)")
		}
		dir = parent
	}
}

// parseGitConfig reads .git/config and extracts the origin remote URL
func parseGitConfig(configPath string) (string, error) {
	file, err := os.Open(configPath)
	if err != nil {
		return "", fmt.Errorf("failed to open git config: %w", err)
	}
	defer func() { _ = file.Close() }()

	scanner := bufio.NewScanner(file)
	inOriginSection := false

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		// Check if we're entering the [remote "origin"] section
		if strings.HasPrefix(line, "[remote") && strings.Contains(line, "origin") {
			inOriginSection = true
			continue
		}

		// Check if we're leaving the section
		if inOriginSection && strings.HasPrefix(line, "[") {
			inOriginSection = false
			continue
		}

		// Extract URL from the origin section
		if inOriginSection && strings.HasPrefix(line, "url") {
			parts := strings.SplitN(line, "=", 2)
			if len(parts) == 2 {
				return strings.TrimSpace(parts[1]), nil
			}
		}
	}

	if err := scanner.Err(); err != nil {
		return "", fmt.Errorf("error reading git config: %w", err)
	}

	return "", fmt.Errorf("no origin remote found in git config")
}

// GetCurrentBranch returns the name of the currently checked-out branch.
func GetCurrentBranch() (string, error) {
	gitDir, err := findGitDir()
	if err != nil {
		return "", err
	}

	headPath := filepath.Join(gitDir, "HEAD")
	data, err := os.ReadFile(headPath)
	if err != nil {
		return "", fmt.Errorf("failed to read .git/HEAD: %w", err)
	}

	headStr := strings.TrimSpace(string(data))
	if strings.HasPrefix(headStr, "ref: refs/heads/") {
		return strings.TrimPrefix(headStr, "ref: refs/heads/"), nil
	}

	return "", fmt.Errorf("HEAD is not on a branch (detached HEAD)")
}

// findGitDir searches for the .git directory starting from the current directory
func findGitDir() (string, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return "", fmt.Errorf("failed to get current directory: %w", err)
	}

	dir := cwd
	for {
		gitDir := filepath.Join(dir, ".git")
		if info, err := os.Stat(gitDir); err == nil && info.IsDir() {
			return gitDir, nil
		}
		parent := filepath.Dir(dir)
		if parent == dir {
			return "", fmt.Errorf("not in a git repository")
		}
		dir = parent
	}
}

// parseRemoteURL extracts owner/name/hostname from various git URL formats:
// - https://codeberg.org/owner/name.git
// - git@codeberg.org:owner/name.git
// - ssh://git@codeberg.org/owner/name.git
func parseRemoteURL(url string) (owner, name, hostname string, err error) {
	url = strings.TrimSpace(url)

	// Remove .git suffix
	url = strings.TrimSuffix(url, ".git")

	// Pattern for HTTPS URLs: https://host/owner/name
	httpsRegex := regexp.MustCompile(`https?://([^/]+)/([^/]+)/([^/]+)`)
	if matches := httpsRegex.FindStringSubmatch(url); len(matches) == 4 {
		return matches[2], matches[3], matches[1], nil
	}

	// Pattern for SSH URLs: git@host:owner/name
	sshRegex := regexp.MustCompile(`git@([^:]+):([^/]+)/(.+)`)
	if matches := sshRegex.FindStringSubmatch(url); len(matches) == 4 {
		return matches[2], matches[3], matches[1], nil
	}

	// Pattern for SSH URLs with protocol: ssh://git@host/owner/name
	sshProtocolRegex := regexp.MustCompile(`ssh://(?:git@)?([^/]+)/([^/]+)/(.+)`)
	if matches := sshProtocolRegex.FindStringSubmatch(url); len(matches) == 4 {
		return matches[2], matches[3], matches[1], nil
	}

	return "", "", "", fmt.Errorf("unable to parse repository from URL: %s", url)
}
