package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
)

func main() {
	token := os.Getenv("TOKEN")
	if token == "" {
		log.Fatalf("TOKEN environment variable not set")
	}

	client := GiteaClient{
		BaseURL: "https://gitea.seeseepuff.be",
		Token:   token,
	}

	org := "archlinux"
	workDir := "./work/"

	err := os.RemoveAll(workDir)
	if err != nil {
		log.Fatalf("failed to remove %s: %v", workDir, err)
	}
	err = os.MkdirAll(workDir, os.ModePerm)
	if err != nil {
		log.Fatalf("failed to create %s: %v", workDir, err)
	}

	repos, err := client.getRepositories(org)
	if err != nil {
		log.Fatalf("Failed to get repositories: %v", err)
	}

	failedRepos := make([]string, 0)
	for _, repo := range repos {
		if repo.Template || strings.Contains(repo.Name, "skip-autobuild") {
			// Skip template repositories
			continue
		}
		wasRepoProcessed := processRepo(&client, workDir, repo)
		if !wasRepoProcessed {
			failedRepos = append(failedRepos, repo.Name)
		}
	}

	if len(failedRepos) > 0 {
		log.Println("The following repos failed to process:")
		for _, repo := range failedRepos {
			log.Printf(" - %s", repo)
		}
		os.Exit(1)
	}
}

func processRepo(client *GiteaClient, workDir string, repo Repository) (success bool) {
	log.Printf("Checking %s", repo.FullName)
	hasFile, err := client.hasPKGBUILD(repo)

	if err != nil {
		log.Printf("Error checking for PKGBUILD: %v", err)
		return false
	}

	if !hasFile {
		log.Println("PKGBUILD not found, skipping")
		return true
	}

	repoPath, err := cloneRepository(client.Token, repo, workDir)
	if err != nil {
		log.Printf("Error cloning repository: %v", err)
		return false
	}

	// Run pre-run script (if it exists)
	requirePush := false
	defer func() {
		if requirePush {
			err = pushRepository(repoPath)
			if err != nil {
				log.Printf("Error pushing repository: %v", err)
				success = false
			}
		}
	}()
	preRunScript := filepath.Join(repoPath, "prerun.sh")
	if _, err := os.Stat(preRunScript); !os.IsNotExist(err) {
		cmd := exec.Command("/bin/sh", preRunScript)
		cmd.Dir = repoPath
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		if err != nil {
			log.Printf("Error running pre-run script: %v", err)
			return false
		}
		requirePush = true
	}

	// Check if the repository is up-to-date
	upToDate, err := checkUpToDate(repoPath)
	if err != nil {
		log.Printf("Error checking if up to date: %v", err)
		return false
	}
	if upToDate {
		log.Printf("%s is up-to-date, skipping", repo.FullName)
		return true
	}

	log.Printf("%s requires bumping, running script", repo.FullName)
	updated, err := bumpRepository(repoPath)
	if err != nil {
		log.Printf("Error bumping repository: %v", err)
		return false
	}
	if updated {
		requirePush = true
	}
	return true
}

func cloneRepository(token string, repo Repository, workDir string) (string, error) {
	targetPath := filepath.Join(workDir, repo.Name)
	cmd := exec.Command("git", "clone", fmt.Sprintf("https://seeseemelk:%s@gitea.seeseepuff.be/archlinux/%s", token, repo.Name), targetPath)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		return "", fmt.Errorf("failed to clone %s: %w", repo.Name, err)
	}
	return targetPath, nil
}

func checkUpToDate(repoPath string) (bool, error) {
	log.Println("Checking if up-to-date")
	cmd := exec.Command("bash", "./check-up-to-date.sh", repoPath)
	output, err := cmd.CombinedOutput()
	log.Println("Command output: ", string(output))
	if err != nil {
		return false, fmt.Errorf("failed to inspect %s: %w", repoPath, err)
	}
	if strings.Index(string(output), "UP-TO-DATE") >= 0 {
		return true, nil
	} else if strings.Index(string(output), "OUT-OF-DATE") >= 0 {
		return false, nil
	}
	return false, errors.New("error parsing command output")
}

/*
Finds the line that says "pkgrel=NUMBER" in the repository's PKGBUILD file,
and increments the number by 1.
*/
func bumpRepository(repoPath string) (bool, error) {
	pkgbuildPath := filepath.Join(repoPath, "PKGBUILD")

	// Read the file
	content, err := os.ReadFile(pkgbuildPath)
	if err != nil {
		log.Fatalf("failed to read %s: %v", pkgbuildPath, err)
	}

	lines := strings.Split(string(content), "\n")
	updated := false

	// Iterate through lines to find and update pkgrel
	for i, line := range lines {
		if strings.HasPrefix(line, "pkgrel=") {
			parts := strings.SplitN(line, "=", 2)
			if len(parts) == 2 {
				// Parse and increment the pkgrel value
				pkgrel, err := strconv.Atoi(strings.TrimSpace(parts[1]))
				if err != nil {
					log.Fatalf("Failed to parse pkgrel value: %v", err)
				}
				lines[i] = fmt.Sprintf("pkgrel=%d", pkgrel+1)
				updated = true
				break
			}
		}
	}

	if !updated {
		return false, fmt.Errorf("pkgrel line not found in PKGBUILD file")
	}

	// Write the updated content back to the PKGBUILD file
	err = os.WriteFile(pkgbuildPath, []byte(strings.Join(lines, "\n")), 0644)
	if err != nil {
		return false, fmt.Errorf("error writing updated PKGBUILD file: %w", err)
	}

	log.Println("PKGBUILD updated, creating commit")

	// Create commit
	cmd := exec.Command("git", "commit", "PKGBUILD", "-m", "Bump pkgrel")
	cmd.Dir = repoPath
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		return false, fmt.Errorf("failed to commit changes: %w", err)
	}

	return true, nil
}

func pushRepository(repoPath string) error {
	// Push commit
	log.Println("Pushing commit")
	cmd := exec.Command("git", "push")
	cmd.Dir = repoPath
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("failed to push changes: %w", err)
	}

	log.Println("Successfully bumped pkgrel in PKGBUILD")
	return nil
}