package main

import (
	"archive/zip"
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/pelletier/go-toml"
)

var backup_locations []string

func parsePacmanConf() (string, error) {
	var err error
	var path string

	file, err := os.Open("/etc/pacman.conf")
	if err != nil {
		return "", err
	}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if !strings.Contains(line, "DBPath") {
			continue
		}
		path = strings.TrimSpace(strings.Split(line, "=")[1])
		break
	}

	err = file.Close()
	if err != nil {
		return "", err
	}

	return fmt.Sprint(path, "local"), nil
}

func zipIt(pathToZip string, pathToFiles string) error {
	var err error

	zipFile, err := os.Create(pathToZip)
	if err != nil {
		return err
	}

	zipWriter := zip.NewWriter(zipFile)

	walker := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		f, err := zipWriter.Create(path[len(pathToFiles):])
		if err != nil {
			return err
		}

		_, err = io.Copy(f, file)
		if err != nil {
			return err
		}

		return nil
	}
	err = filepath.Walk(pathToFiles, walker)
	if err != nil {
		return err
	}

	err = zipWriter.Close()
	if err != nil {
		return err
	}
	err = zipFile.Close()
	if err != nil {
		return err
	}

	return nil
}

func backup(when string) error {
	var err error
	tmpPath := fmt.Sprint("/tmp/update/", when, "_backup")
	pacmanDb, err := parsePacmanConf()
	if err != nil {
		return err
	}

	err = os.MkdirAll(tmpPath, 0755)
	if err != nil {
		return err
	}
	defer func() {
		os.RemoveAll("/tmp/update")
	}()

	output, err := exec.Command("pacman",  "--verbose", "--query").Output()
	if err != nil {
		return err
	}

	err = os.WriteFile(fmt.Sprint(tmpPath, fmt.Sprint(when, "_pacman.txt")), output, 0644)
	if err != nil {
		return err
	}

	if when != "post" {
		err = CopyDir(pacmanDb, fmt.Sprint(tmpPath, "/", pacmanDb))
		if err != nil {
			return err
		}
	}

	current_time := time.Now().Format(time.RFC3339)
	pathToZip := fmt.Sprint(tmpPath, "_", current_time, ".zip")
	err = zipIt(pathToZip, tmpPath)
	if err != nil {
		return err
	}

	for _, backup_location := range backup_locations {
		err = CopyFile(pathToZip, fmt.Sprint(backup_location, "/",  when, "_backup_", current_time, ".zip"))
		if err != nil {
			return err
		}
	}

	return nil
}

func main() {
	os.RemoveAll("/tmp/update")

	tree, err := toml.LoadFile("/etc/update.toml")
	if err != nil {
		fmt.Println("backup failed for the following reason: ", err)
		os.Exit(1)
	}

	backup_locations = tree.GetArray("backup.locations").([]string)

	err = backup(os.Args[1])
	if err != nil {
		fmt.Println("backup failed for the following reason: ", err)
		os.Exit(1)
	}
}
