package forgepb

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"go.wit.com/lib/protobuf/gitpb"
	"go.wit.com/log"
)

func (f *Forge) MakeDevelPatchSet() (*Patchset, error) {
	pset := new(Patchset)
	dir, err := os.MkdirTemp("", "forge")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(dir) // clean up
	pset.TmpDir = dir

	all := f.Repos.SortByFullPath()
	for all.Scan() {
		repo := all.Next()
		userb := repo.GetUserBranchName()
		develb := repo.GetDevelBranchName()

		if develb == "" {
			continue
		}
		if userb == "" {
			continue
		}
		pset.StartBranchName = develb
		pset.EndBranchName = userb
		err := pset.makePatchSetNew(repo)
		if err != nil {
			return nil, err
		}
	}
	return pset, nil
}

func (f *Forge) MakeMasterPatchSet() (*Patchset, error) {
	pset := new(Patchset)
	dir, err := os.MkdirTemp("", "forge")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(dir) // clean up
	pset.TmpDir = dir

	all := f.Repos.SortByFullPath()
	for all.Scan() {
		repo := all.Next()
		startb := repo.GetMasterBranchName()
		endb := repo.GetUserBranchName()

		if startb == "" {
			continue
		}
		if endb == "" {
			continue
		}
		// log.Info("repo", repo.GetGoPath(), startb, "..", endb)
		pset.StartBranchName = startb
		pset.EndBranchName = endb
		err := pset.makePatchSetNew(repo)
		if err != nil {
			return nil, err
		}
	}
	return pset, nil
}

func (pset *Patchset) makePatchSetNew(repo *gitpb.Repo) error {
	startBranch := pset.StartBranchName
	endBranch := pset.EndBranchName
	repoDir := filepath.Join(pset.TmpDir, repo.GetGoPath())
	err := os.MkdirAll(repoDir, 0755)
	if err != nil {
		return err
	}

	// git format-patch branch1..branch2
	cmd := []string{"git", "format-patch", "-o", repoDir, startBranch + ".." + endBranch}
	r := repo.Run(cmd)
	if r.Error != nil {
		log.Info("git format-patch", repo.FullPath)
		log.Info("git format-patch", cmd)
		log.Info("git format-patch error", r.Error)
		return r.Error
	}
	if r.Exit != 0 {
		log.Info("git format-patch", repo.FullPath)
		log.Info("git format-patch", cmd)
		log.Info("git format-patch exit", r.Exit)
		return errors.New(fmt.Sprintf("git returned %d", r.Exit))
	}
	if len(r.Stdout) == 0 {
		// git created no files to add
		return nil
	}

	return pset.addPatchFiles(repo)
}

// process each file in pDir/
func (p *Patchset) addPatchFiles(repo *gitpb.Repo) error {
	psetDir := repo.GetGoPath()
	tmpDir := p.TmpDir
	log.Info("ADD PATCH FILES ADDED DIR", tmpDir)
	fullDir := filepath.Join(tmpDir, psetDir)
	var baderr error
	filepath.Walk(fullDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			// Handle possible errors, like permission issues
			fmt.Fprintf(os.Stderr, "error accessing path %q: %v\n", path, err)
			baderr = err
			return err
		}

		if info.IsDir() {
			return nil
		}
		log.Info("IS THIS A FULL PATH ?", path)
		log.Info("trim this from path ?", fullDir)
		log.Info("trim this from path ?", psetDir)
		log.Info("trim this from path ?", tmpDir)
		data, err := os.ReadFile(path)
		if err != nil {
			log.Info("addPatchFile() failed", path)
			baderr = err
			return err
		}
		patch := new(Patch)
		patch.Filename, _ = filepath.Rel(p.TmpDir, path)
		patch.Data = data
		patch.parseData()
		patch.StartHash = repo.DevelHash()
		p.Patches = append(p.Patches, patch)
		log.Info("ADDED PATCH FILE", path)
		return nil
	})
	return baderr
}

// looks at the git format-patch output
// saves the commit Hash
// saves the diff lines
func (p *Patch) parseData() string {
	lines := strings.Split(string(p.Data), "\n")
	for _, line := range lines {
		fields := strings.Fields(line)
		if len(fields) < 2 {
			continue
		}
		switch fields[0] {
		case "From":
			p.CommitHash = fields[1]
		case "diff":
			p.Files = append(p.Files, line)
		}
	}
	return ""
}

// just an example of how to walk only directories
func onlyWalkDirs(pDir string) error {
	log.Info("DIR", pDir)
	// var all []string
	var baderr error
	filepath.WalkDir(pDir, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			// Handle possible errors, like permission issues
			fmt.Fprintf(os.Stderr, "error accessing path %q: %v\n", path, err)
			baderr = err
			return err
		}

		log.Info("TESTING DIR", path)
		if d.IsDir() {
			return filepath.SkipDir
		}
		log.Info("NEVER GETS HERE? WHAT IS THIS?", path)
		return nil
	})
	return baderr
}