package common

import (
	"fmt"
	"io"
	"io/fs"
	"log"
	"os"
	"sync"
	"time"
)

// From https://opensource.com/article/18/6/copying-files-go
func CopyFile(src string, dst string) error {
	source, err := os.Open(src)
	if err != nil {
		return err
	}
	defer source.Close()

	destination, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer destination.Close()
	_, err = io.Copy(destination, source)
	return err
}

func Copy(opts CopyOpts) {
	if !opts.srcInfo.Mode().IsRegular() {
		log.Fatalf("%s is not a regular file", opts.src)
	}

	opModifier := ""
	const fallback = " (fallback)"

	if opts.hardlink {
		// hardlink this file
		if opts.verbose {
			fmt.Printf("hardlink%s %v => %v\n", opModifier, opts.src, opts.dst)
		}
		err := os.Link(opts.src, opts.dst)
		if err != nil {
			if opts.verbose {
				fmt.Printf("hardlink failed: %v\n", err)
				opModifier = fallback
			}
			// fallback to clonefile or copy
		} else {
			return
		}
	}

	// clone this file
	if opts.verbose {
		fmt.Printf("clonefile%s %v => %v\n", opModifier, opts.src, opts.dst)
	}
	err := CloneFile(opts.src, opts.dst)
	if err == nil {
		return
	}

	if opts.verbose {
		fmt.Printf("clonefile failed: %v\n", err)
		fmt.Printf("copy%s %v => %v\n", opModifier, opts.src, opts.dst)
		opModifier = fallback
	}

	// copy this file
	err = CopyFile(opts.src, opts.dst)
	if err != nil {
		log.Fatal(err)
	}

	if opts.preserveMTime {
		accessTime := time.Now()
		err := os.Chtimes(opts.dst, accessTime, opts.srcInfo.ModTime())
		if err != nil {
			log.Fatal(err)
		}
	}
}

type CopyWorker struct {
	queue <-chan CopyOpts
}

func NewCopyWorker(queue <-chan CopyOpts) *CopyWorker {
	return &CopyWorker{queue: queue}
}

func (w *CopyWorker) Run(wg *sync.WaitGroup) {
	defer wg.Done()
	for opts := range w.queue {
		Copy(opts)
	}
}

type CopyOpts struct {
	src, dst      string
	srcInfo       fs.FileInfo
	hardlink      bool
	verbose       bool
	preserveMTime bool
}

func NewCopyOpts(src string, dst string, srcInfo fs.FileInfo, hardlink bool, verbose bool, preserveMTime bool) CopyOpts {
	return CopyOpts{src: src, dst: dst, srcInfo: srcInfo, hardlink: hardlink, verbose: verbose, preserveMTime: preserveMTime}
}
