package src

import (
	"fmt"
	"os"
	"path"
	"regexp"
	"strings"

	"github.com/pkg/errors"
)

var (
	NwoRegExp        = regexp.MustCompile(`^[^/\s]+/[^/\s]+$`)
	ErrEmptyRepoList = errors.New("repo list cannot be empty")
	ErrEmptyCacheDir = errors.New("cache directory contains no actions to sync")
)

func getRepoNamesFromRepoFlags(flags *CommonFlags) ([]string, error) {
	if flags.RepoNameList != "" {
		return getRepoNamesFromCSVString(flags.RepoNameList)
	}

	if flags.RepoNameListFile != "" {
		return getRepoNamesFromFile(flags.RepoNameListFile)
	}

	if flags.RepoName != "" {
		return []string{flags.RepoName}, nil
	}

	return nil, nil
}

func getRepoNamesFromCacheDir(flags *CommonFlags) ([]string, error) {
	repoNames := make([]string, 0)

	orgDirs, err := os.ReadDir(flags.CacheDir)
	if err != nil {
		return nil, errors.Wrapf(err, "error opening cache directory `%s`", flags.CacheDir)
	}
	for _, orgDir := range orgDirs {
		orgDirPath := path.Join(flags.CacheDir, orgDir.Name())
		if !orgDir.IsDir() {
			return nil, errors.Errorf("unexpected file in root of cache directory `%s`", orgDirPath)
		}
		repoDirs, err := os.ReadDir(orgDirPath)
		if err != nil {
			return nil, errors.Wrapf(err, "error opening repository cache directory `%s`", orgDirPath)
		}
		for _, repoDir := range repoDirs {
			nwo := fmt.Sprintf("%s/%s", orgDir.Name(), repoDir.Name())
			repoNames = append(repoNames, nwo)
		}
	}

	if len(repoNames) == 0 {
		return nil, ErrEmptyCacheDir
	}

	return repoNames, nil
}

func getRepoNamesFromCSVString(csv string) ([]string, error) {
	repos := filterEntries(strings.Split(csv, ","))
	if len(repos) == 0 {
		return nil, ErrEmptyRepoList
	}
	return repos, nil
}

func getRepoNamesFromFile(file string) ([]string, error) {
	data, err := os.ReadFile(file)
	if err != nil {
		return nil, err
	}
	repos := filterEntries(strings.Split(string(data), "\n"))
	if len(repos) == 0 {
		return nil, ErrEmptyRepoList
	}
	return repos, nil
}

func filterEntries(names []string) []string {
	filtered := []string{}
	for _, name := range names {
		if !strings.HasPrefix(name, "#") && len(name) > 0 {
			filtered = append(filtered, name)
		}
	}
	return filtered
}

func extractSourceDest(repoName string) (string, string, error) {
	repoNameParts := strings.Split(repoName, ":")
	if len(repoNameParts) > 2 {
		return "", "", fmt.Errorf("`%s` is not a valid repo name. Use a single colon to separate source and destination arguments. Example: `upstream_owner/upstream_repo:destination_owner/destination_repo`", repoName)
	}

	originNwo, err := validateNwo(repoNameParts[0])
	if err != nil {
		return "", "", err
	}

	destNwo := originNwo
	if len(repoNameParts) > 1 {
		destNwo, err = validateNwo(repoNameParts[1])
		if err != nil {
			return "", "", err
		}
	}

	return originNwo, destNwo, nil
}

func validateNwo(nwo string) (string, error) {
	s := strings.TrimSpace(nwo)
	if NwoRegExp.MatchString(s) {
		return s, nil
	}
	return "", fmt.Errorf("`%s` is not a valid repo name", s)
}

func splitNwo(nwo string) (string, string, error) {
	nwoParts := strings.Split(nwo, "/")
	if len(nwoParts) != 2 {
		return "", "", fmt.Errorf("`%s` is not a valid repo name", nwo)
	}

	return nwoParts[0], nwoParts[1], nil
}
