package repo

import (
	"maps"
	"slices"
	"sort"
	"strings"

	"tangled.org/core/appview/db"
	"tangled.org/core/appview/models"
	"tangled.org/core/orm"
	"tangled.org/core/types"
)

func sortFiles(files []types.NiceTree) {
	sort.Slice(files, func(i, j int) bool {
		iIsFile := files[i].IsFile()
		jIsFile := files[j].IsFile()
		if iIsFile != jIsFile {
			return !iIsFile
		}
		return files[i].Name < files[j].Name
	})
}

func sortBranches(branches []types.Branch) {
	slices.SortFunc(branches, func(a, b types.Branch) int {
		if a.IsDefault {
			return -1
		}
		if b.IsDefault {
			return 1
		}
		if a.Commit != nil && b.Commit != nil {
			if a.Commit.Committer.When.Before(b.Commit.Committer.When) {
				return 1
			} else {
				return -1
			}
		}
		return strings.Compare(a.Name, b.Name)
	})
}

func uniqueEmails(commits []types.Commit) []string {
	emails := make(map[string]struct{})
	for _, commit := range commits {
		emails[commit.Author.Email] = struct{}{}
		emails[commit.Committer.Email] = struct{}{}
		for _, c := range commit.CoAuthors() {
			emails[c.Email] = struct{}{}
		}
	}

	// delete empty emails if any, from the set
	delete(emails, "")

	return slices.Collect(maps.Keys(emails))
}

func balanceIndexItems(commitCount, branchCount, tagCount, fileCount int) (commitsTrunc int, branchesTrunc int, tagsTrunc int) {
	if commitCount == 0 && tagCount == 0 && branchCount == 0 {
		return
	}

	// typically 1 item on right side = 2 files in height
	availableSpace := fileCount / 2

	// clamp tagcount
	if tagCount > 0 {
		tagsTrunc = 1
		availableSpace -= 1 // an extra subtracted for headers etc.
	}

	// clamp branchcount
	if branchCount > 0 {
		branchesTrunc = min(max(branchCount, 1), 3)
		availableSpace -= branchesTrunc // an extra subtracted for headers etc.
	}

	// show
	if commitCount > 0 {
		commitsTrunc = max(availableSpace, 3)
	}

	return
}

// grab pipelines from DB and munge that into a hashmap with commit sha as key
//
// golang is so blessed that it requires 35 lines of imperative code for this
func getPipelineStatuses(
	d *db.DB,
	repo *models.Repo,
	shas []string,
) (map[string]models.Pipeline, error) {
	m := make(map[string]models.Pipeline)

	if len(shas) == 0 {
		return m, nil
	}

	ps, err := db.GetPipelineStatuses(
		d,
		len(shas),
		orm.FilterEq("p.repo_owner", repo.Did),
		orm.FilterEq("p.repo_name", repo.Name),
		orm.FilterEq("p.knot", repo.Knot),
		orm.FilterIn("p.sha", shas),
	)
	if err != nil {
		return nil, err
	}

	for _, p := range ps {
		m[p.Sha] = p
	}

	return m, nil
}
