package pkg

import (
	"crypto/md5"
	"fmt"
	"path/filepath"
	"sync"

	"gitlab.com/M0M097/pkg/cli"
	"gitlab.com/M0M097/pkg/internal/filecheck"
	"gitlab.com/M0M097/pkg/sborepo"
)

// filesToDownload returns a slice of URLs to download for package sources that
// are not already downloaded or, when enabled, have different MD5 sums.
func filesToDownload(dg sborepo.DependencyGraph, tmpDir string, checkMD5Sum bool) []string {
	var (
		filesToDownload []string
		mu              sync.Mutex
	)
	sborepo.Parallel(dg.ProcessUnordered)(func(pkg *sborepo.Package) {
		var (
			md5Sums       = pkg.MD5Sums()
			urls          = pkg.DownloadURLs()
			downloadNames = pkg.DownloadNames()
			wg            sync.WaitGroup
		)
		wg.Add(len(downloadNames))
		for i, downloadName := range downloadNames {
			i, downloadName := i, downloadName
			checksum := ""
			if i < len(md5Sums) {
				checksum = md5Sums[i]
			}
			go func() {
				defer wg.Done()
				if match, _ := downloadMatches(
					filepath.Join(tmpDir, downloadName), checksum, checkMD5Sum,
				); !match {
					mu.Lock()
					filesToDownload = append(filesToDownload, urls[i])
					mu.Unlock()
				}
			}()
		}
		wg.Wait()
	})
	return filesToDownload
}

func downloadMatches(fullPath, checksum string, checkMD5Sum bool) (bool, error) {
	if !checkMD5Sum {
		return filecheck.Exists(fullPath), nil
	}
	return filecheck.HashMatches(fullPath, checksum, md5.New())
}

func checkDownloads(
	dg sborepo.DependencyGraph,
	tmpDir string,
	checkMD5Sum bool,
	progress cli.ProgressReporter,
) bool {
	ok := true
	dg.ProcessTopological(func(pkg *sborepo.Package) {
		md5Sums := pkg.MD5Sums()
		for i, downloadName := range pkg.DownloadNames() {
			var (
				fullPath = filepath.Join(tmpDir, downloadName)
				checksum string
			)
			if i < len(md5Sums) {
				checksum = md5Sums[i]
			}
			match, err := downloadMatches(fullPath, checksum, checkMD5Sum)
			var msg string
			switch {
			case err != nil:
				msg = err.Error()
			case !match && checkMD5Sum:
				msg = fmt.Sprintf(
					"%s: MD5Sum Mismatch. Run with -%s to disable MD5Sum Check",
					downloadName, MD5_FLAG,
				)
			case !match:
				msg = fullPath + ": no such file or directory"
			default:
				continue
			}
			ok = false
			skipDependents(pkg, dg, msg, progress)
		}
	})
	return ok
}
