package forgepb

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

	"github.com/destel/rill"
	"go.wit.com/lib/protobuf/gitpb"
	"go.wit.com/log"
)

func (f *Forge) ScanGoSrc() (bool, error) {
	dirs, err := gitDirectoriesNew(f.goSrc)
	if err != nil {
		return false, err
	}

	var gopaths []string
	for _, dir := range dirs {
		// log.Info("forge.ScanGoSrc()", dir)
		if strings.HasPrefix(dir, f.goSrc) {
			gopath := strings.TrimPrefix(dir, f.goSrc)
			gopath = strings.Trim(gopath, "/")
			if r := f.FindByGoPath(gopath); r != nil {
				// log.Info("already have", gopath)
				continue
			}
			gopaths = append(gopaths, gopath)
		} else {
			log.Log(WARN, "ScanGoSrc() bad:", dir)
			return false, errors.New("forgepb.ScanGoSrc() bad dir: " + dir)
		}
	}
	newcount, err := f.rillScanDirs(gopaths)
	if err != nil {
		log.Info("go src dir problem. exit for now?", err)
		return false, err
	}
	if newcount != 0 {
		log.Info("forge go src scan found", newcount, "repos")
		f.SetConfigSave(true)
	}
	return true, err
}

func (f *Forge) ScanDir(dir string) *gitpb.Repo {
	// repo, err := f.NewGoRepo(gopath, "")
	repo, err := f.Repos.NewGoRepo(dir, "")
	log.Info("need to implement ScanDir()", dir, err)
	return repo
}

// doesn't enter the directory any further when it finds a .git/
// not stupid like my old version
func gitDirectoriesNew(srcDir string) ([]string, error) {
	var all []string
	var trip bool
	err := filepath.WalkDir(srcDir, 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)
			return err
		}

		if d.IsDir() {
			// log.Info("path is dir", path)
		} else {
			_, fname := filepath.Split(path)
			switch fname {
			case "repos.pb":
			case "go.work":
			case "go.work.last":
			default:
				// todo: figure out a way to do padding for init()
				if trip == false {
					log.Info("WARNING:")
				}
				log.Info("WARNING: you have an untracked file outside of any .git repository:", path)
				trip = true
			}
			return nil
		}

		gitdir := filepath.Join(path, ".git")
		_, err2 := os.Stat(gitdir)
		if !os.IsNotExist(err2) {
			all = append(all, path)
			return filepath.SkipDir
		}
		return nil
	})
	//
	// probably always leave this here forever
	// this check, along with CheckDirty() makes sure you can safely delete ~/go/src or the go.work directory
	// because everything is either checked in or deleted. An important thing to know!
	if trip {
		log.Info("WARNING:")
		log.Info("WARNING: there isn't a way to disable this warning yet")
		log.Info("WARNING: probably this is a good thing however. you don't want to leave files outside of git repos here")
		log.Info("WARNING: so this warning should probably stay")
		log.Info("WARNING:")
		log.Info("WARNING: this also might mean you put these files here because you are actively working on them")
		log.Info("WARNING: and you don't want to forget about them")
		log.Info("WARNING:")
	}
	return all, err
}

func gitDirectoriesOld(srcDir string) ([]string, error) {
	var all []string
	err := filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Log(WARN, "Error accessing path:", path, err)
			return nil
		}

		// Check if the path is a directory and has a .git subdirectory
		if info.IsDir() && IsGitDir(path) {
			all = append(all, path)
		}

		return nil
	})

	if err != nil {
		log.Log(WARN, "Error walking the path:", srcDir, err)
	}

	return all, err
}

// IsGitDir checks if a .git directory exists inside the given directory
func IsGitDir(dir string) bool {
	gitDir := filepath.Join(dir, ".git")
	info, err := os.Stat(gitDir)
	if os.IsNotExist(err) {
		return false
	}
	return info.IsDir()
}

// rill is awesome. long live rill
// attempt scan with rill
func (f *Forge) rillScanDirs(gopaths []string) (int, error) {
	// Convert a slice of user IDs into a channel
	ids := rill.FromSlice(gopaths, nil)

	// Read users from the API.
	// Concurrency = 20
	dirs := rill.Map(ids, 20, func(id string) (*gitpb.Repo, error) {
		return f.checkpath(id, "")
	})

	var counter int
	// Activate users.
	// Concurrency = 10
	err := rill.ForEach(dirs, 10, func(repo *gitpb.Repo) error {
		counter += 1
		return nil
	})

	return counter, err
}

func (f *Forge) checkpath(gopath string, url string) (*gitpb.Repo, error) {
	fullpath := filepath.Join(f.GetGoSrc(), gopath)
	log.Info("forge creating protobuf for", fullpath)
	repo, err := f.NewGoRepo(gopath, "")
	if err != nil {
		log.Info("\tprotobuf error", gopath, err)
	}
	return repo, err
}