// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT

package f3

import (
	"context"
	"path/filepath"
	"slices"
	"sort"
	"testing"

	"code.forgejo.org/f3/gof3/v3/f3"
	"code.forgejo.org/f3/gof3/v3/kind"
	"code.forgejo.org/f3/gof3/v3/options"
	"code.forgejo.org/f3/gof3/v3/path"
	f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
	"code.forgejo.org/f3/gof3/v3/tree/generic"
)

var KindToFixturePath = map[kind.Kind]string{
	f3_tree.KindTopics:         "/forge/topics",
	f3_tree.KindTopic:          "/forge/topics/14411441",
	f3_tree.KindUsers:          "/forge/users",
	f3_tree.KindUser:           "/forge/users/10111",
	f3_tree.KindProjects:       "/forge/users/10111/projects",
	f3_tree.KindProject:        "/forge/users/10111/projects/74823",
	f3_tree.KindLabels:         "/forge/users/10111/projects/74823/labels",
	f3_tree.KindLabel:          "/forge/users/10111/projects/74823/labels/7777",
	f3_tree.KindIssues:         "/forge/users/10111/projects/74823/issues",
	f3_tree.KindIssue:          "/forge/users/10111/projects/74823/issues/1234567",
	f3_tree.KindPullRequests:   "/forge/users/10111/projects/74823/pull_requests",
	f3_tree.KindPullRequest:    "/forge/users/10111/projects/74823/pull_requests/2222",
	f3_tree.KindReviews:        "/forge/users/10111/projects/74823/pull_requests/2222/reviews",
	f3_tree.KindReview:         "/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593",
	f3_tree.KindReviewComments: "/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593/reviewcomments",
	f3_tree.KindReviewComment:  "/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593/reviewcomments/9876543",
	f3_tree.KindMilestones:     "/forge/users/10111/projects/74823/milestones",
	f3_tree.KindMilestone:      "/forge/users/10111/projects/74823/milestones/7888",
	f3_tree.KindReactions:      "/forge/users/10111/projects/74823/issues/1234567/reactions",
	f3_tree.KindReaction:       "/forge/users/10111/projects/74823/issues/1234567/reactions/1212",
	f3_tree.KindComments:       "/forge/users/10111/projects/74823/issues/1234567/comments",
	f3_tree.KindComment:        "/forge/users/10111/projects/74823/issues/1234567/comments/1111999",
	f3_tree.KindRepositories:   "/forge/users/10111/projects/74823/repositories",
	f3_tree.KindRepository:     "/forge/users/10111/projects/74823/repositories/vcs",
	f3_tree.KindReleases:       "/forge/users/10111/projects/74823/releases",
	f3_tree.KindRelease:        "/forge/users/10111/projects/74823/releases/123",
	f3_tree.KindAttachments:    "/forge/users/10111/projects/74823/releases/123/attachments",
	f3_tree.KindAttachment:     "/forge/users/10111/projects/74823/releases/123/attachments/585858",
	f3_tree.KindOrganizations:  "/forge/organizations",
	f3_tree.KindOrganization:   "/forge/organizations/3330001",
}

var KindWithFixturePath = SetKindWithFixturePath()

func SetKindWithFixturePath() []kind.Kind {
	l := make([]kind.Kind, 0, len(KindToFixturePath))

	for kind := range KindToFixturePath {
		l = append(l, kind)
	}
	sort.Slice(l, func(i, j int) bool { return string(l[i]) < string(l[j]) })
	return l
}

func TreeBuild(t *testing.T, name string, opts options.Interface, tree generic.TreeInterface) {
	TreeBuildPartial(t, name, []kind.Kind{}, opts, tree)
}

func TreeBuildPartial(t *testing.T, name string, exceptions []kind.Kind, opts options.Interface, tree generic.TreeInterface) {
	ctx := context.Background()

	creator := NewCreator(t, name, tree.GetLogger())

	f3Tree := tree.(f3_tree.TreeInterface)
	url := "<unknown>"
	if urlInterface, ok := opts.(options.URLInterface); ok {
		url = urlInterface.GetURL()
	}

	f3Tree.CreateChild(ctx, "/", func(parent path.Path, forge generic.NodeInterface) {
		f := creator.GenerateForge()
		f.URL = url
		forge.FromFormat(f)
	})

	if slices.Contains(exceptions, f3_tree.KindUsers) {
		return
	}

	f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindUsers], func(parent path.Path, user generic.NodeInterface) {
		user.FromFormat(GeneratorSetID(creator.GenerateUser(), "10111"))
	})

	if slices.Contains(exceptions, f3_tree.KindProjects) {
		return
	}
	f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindProjects], func(parent path.Path, project generic.NodeInterface) {
		project.FromFormat(GeneratorSetID(creator.GenerateProject(), "74823"))
	})

	if slices.Contains(exceptions, f3_tree.KindRepositories) {
		return
	}
	f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindRepositories], func(parent path.Path, repository generic.NodeInterface) {
		repository.FromFormat(GeneratorSetID(creator.GenerateRepository(f3.RepositoryNameDefault), f3.RepositoryNameDefault))
	})

	if !slices.Contains(exceptions, f3_tree.KindReleases) {

		f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindReleases], func(parent path.Path, release generic.NodeInterface) {
			release.FromFormat(GeneratorSetID(creator.GenerateRelease(parent), "123"))
		})

		if !slices.Contains(exceptions, f3_tree.KindAttachments) {
			f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindAttachments], func(parent path.Path, attachment generic.NodeInterface) {
				attachment.FromFormat(GeneratorSetID(creator.GenerateAttachment(parent), "585858"))
			})
		}
	}

	reviewerID := "20222"
	{
		userID := "20222"
		f3Tree.CreateChild(ctx, "/forge/users", func(parent path.Path, user generic.NodeInterface) {
			user.FromFormat(GeneratorSetID(creator.GenerateUser(), userID))
		})
		projectID := "99099"
		f3Tree.CreateChild(ctx, filepath.Join("/forge/users", userID, "projects"), func(parent path.Path, user generic.NodeInterface) {
			user.FromFormat(GeneratorSetID(creator.GenerateForkedProject(parent, KindToFixturePath[f3_tree.KindProject]), projectID))
		})
		f3Tree.CreateChild(ctx, filepath.Join("/forge/users", userID, "projects", projectID, "repositories"), func(parent path.Path, repository generic.NodeInterface) {
			repository.FromFormat(GeneratorSetID(creator.GenerateRepository(f3.RepositoryNameDefault), f3.RepositoryNameDefault))
		})
	}

	if !slices.Contains(exceptions, f3_tree.KindPullRequests) {
		f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindPullRequests], func(parent path.Path, pullRequest generic.NodeInterface) {
			pullRequest.FromFormat(GeneratorSetID(creator.GeneratePullRequest(parent), "2222"))
		})

		if !slices.Contains(exceptions, f3_tree.KindReviews) {
			f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindReviews], func(parent path.Path, review generic.NodeInterface) {
				reviewFormat := creator.GenerateReview(parent)
				GeneratorSetID(reviewFormat, "4593")
				reviewFormat.ReviewerID = f3_tree.NewUserReference(reviewerID)
				review.FromFormat(reviewFormat)
			})

			if !slices.Contains(exceptions, f3_tree.KindReviewComments) {
				f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindReviewComments], func(parent path.Path, reviewComment generic.NodeInterface) {
					reviewCommentFormat := creator.GenerateReviewComment(parent)
					GeneratorSetID(reviewCommentFormat, "9876543")
					reviewCommentFormat.PosterID = f3_tree.NewUserReference(reviewerID)
					reviewComment.FromFormat(reviewCommentFormat)
				})
			}
		}
	}

	if !slices.Contains(exceptions, f3_tree.KindLabels) {
		f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindLabels], func(parent path.Path, label generic.NodeInterface) {
			label.FromFormat(GeneratorSetID(creator.GenerateLabel(), "99999"))
		})
		f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindLabels], func(parent path.Path, label generic.NodeInterface) {
			label.FromFormat(GeneratorSetID(creator.GenerateLabel(), "7777"))
		})
	}

	if !slices.Contains(exceptions, f3_tree.KindMilestones) {
		f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindMilestones], func(parent path.Path, milestone generic.NodeInterface) {
			milestone.FromFormat(GeneratorSetID(creator.GenerateMilestone(), "7888"))
		})
	}

	if !slices.Contains(exceptions, f3_tree.KindIssues) {
		f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindIssues], func(parent path.Path, issue generic.NodeInterface) {
			issue.FromFormat(GeneratorSetID(creator.GenerateIssue(parent), "1234567"))
		})

		if !slices.Contains(exceptions, f3_tree.KindComments) {
			f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindComments], func(parent path.Path, comment generic.NodeInterface) {
				comment.FromFormat(GeneratorSetID(creator.GenerateComment(parent), "1111999"))
			})

			if !slices.Contains(exceptions, f3_tree.KindReactions) {
				f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindReactions], func(parent path.Path, reaction generic.NodeInterface) {
					reaction.FromFormat(GeneratorSetID(creator.GenerateReaction(parent), "1212"))
				})
			}
		}
	}

	if !slices.Contains(exceptions, f3_tree.KindOrganizations) {
		f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindOrganizations], func(parent path.Path, organization generic.NodeInterface) {
			organization.FromFormat(GeneratorSetID(creator.GenerateOrganization(), "3330001"))
		})
	}

	if !slices.Contains(exceptions, f3_tree.KindTopics) {
		f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindTopics], func(parent path.Path, topic generic.NodeInterface) {
			topic.FromFormat(GeneratorSetID(creator.GenerateTopic(), "14411441"))
		})
	}
}

func TreeDelete(t *testing.T, nonTestUsers []string, options options.Interface, tree generic.TreeInterface) {
	ctx := context.Background()

	for _, owners := range []path.Path{f3_tree.OrganizationsPath, f3_tree.UsersPath} {
		for _, owner := range tree.Find(owners).List(ctx) {
			if user, ok := owner.ToFormat().(*f3.User); ok {
				if slices.Contains(nonTestUsers, user.UserName) {
					continue
				}
			}
			for _, project := range owner.Find(generic.NewPathFromString(f3_tree.KindProjects)).List(ctx) {
				project.Delete(ctx)
			}
			owner.Delete(ctx)
		}
	}
}
