package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"
	"crypto/tls"
	"sync"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
)

var commitmu sync.Mutex

func commit(
	tree *git.Worktree,
	padfile string,
	url string,
) (plumbing.Hash, error) {
	commitmu.Lock()
	defer commitmu.Unlock()

	commit, err := tree.Commit(
		fmt.Sprintf("Updated %s from %s", padfile, url),
		&git.CommitOptions{
			Author: &object.Signature {
				Name: "Pad Archiver",
				Email: "pad-archiver@dadada.li",
				When: time.Now(),
			},
		},
	)

	if err != nil {
		return plumbing.ZeroHash, fmt.Errorf("Failed to commit %s: %w", padfile, err)
	}

	return commit, nil
}

func update(
	tree *git.Worktree,
	url string,
) (plumbing.Hash, error) {
	res, err := http.Get(url + "/export/txt")
	if err != nil {
		return plumbing.ZeroHash, fmt.Errorf("Failed to get pad at %s: %w", url, err)
	}

	defer res.Body.Close()

	padfile := path.Base(url) + ".txt"

	padpath := filepath.Join(tree.Filesystem.Root(), padfile)
	out, err := os.Create(padpath)

	written, err := io.Copy(out, res.Body)
	if err != nil {
		return plumbing.ZeroHash, fmt.Errorf("Failed to write pad to file at %s: %w", padfile, err)
	}

	if written < 100 {
		return plumbing.ZeroHash, fmt.Errorf("Skipping update of %s, because pad has likely been removed from %s", padfile, url)
	}

	status, err := tree.Status()
	if err != nil {
		return plumbing.ZeroHash, fmt.Errorf("Failed to get status of %s: %w", padfile, err)
	}

	if status.IsClean() {
		return plumbing.ZeroHash, fmt.Errorf("No changes recorded for %s", url)
	}

	if _, err = tree.Add(padfile); err != nil {
		return plumbing.ZeroHash, fmt.Errorf("Failed to stage %s: %w", padfile, err)
	}

	return commit(tree, padfile, url)
}

func main() {
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	cwd, err := os.Getwd()
	if err != nil {
		log.Fatalf("Failed to get working directory %s", err)
	}

	gitdir := flag.String(
		"C",
		cwd,
		"git directory",
	)
	flag.Parse()

	repo, err := git.PlainOpen(*gitdir)
	if err != nil {
		log.Fatalf("Failed to open git repo %s: %s", *gitdir, err)
	}

	tree, err := repo.Worktree()
	if err != nil {
		log.Fatalf("Failed to open git worktree %s", err)
	}

	scanner := bufio.NewScanner(os.Stdin)

	var wg sync.WaitGroup
	for scanner.Scan() {
		wg.Add(1)
		padurl := scanner.Text()

		go func() {
			defer wg.Done()
			if _, err := update(tree, padurl); err != nil {
				log.Printf("%s", err)
			} else {
				log.Printf("Updated %s", padurl)
			}
		}()
	}
	wg.Wait()

	tree.Clean(&git.CleanOptions{})
}