// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package doctor

import (
	"context"

	"code.gitea.io/gitea/models/db"
	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/storage"
	repo_service "code.gitea.io/gitea/services/repository"

	"xorm.io/builder"
)

func handleDeleteOrphanedRepos(ctx context.Context, logger log.Logger, autofix bool) error {
	test := &consistencyCheck{
		Name:         "Repos with no existing owner",
		Counter:      countOrphanedRepos,
		Fixer:        deleteOrphanedRepos,
		FixedMessage: "Deleted all content related to orphaned repos",
	}
	return test.Run(ctx, logger, autofix)
}

// countOrphanedRepos count repository where user of owner_id do not exist
func countOrphanedRepos(ctx context.Context) (int64, error) {
	return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=`user`.id")
}

// deleteOrphanedRepos delete repository where user of owner_id do not exist
func deleteOrphanedRepos(ctx context.Context) (int64, error) {
	if err := storage.Init(); err != nil {
		return 0, err
	}

	batchSize := db.MaxBatchInsertSize("repository")
	e := db.GetEngine(ctx)
	var deleted int64
	adminUser := &user_model.User{IsAdmin: true}

	for {
		var ids []int64
		if err := e.Table("`repository`").
			Join("LEFT", "`user`", "repository.owner_id=`user`.id").
			Where(builder.IsNull{"`user`.id"}).
			Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
			return deleted, err
		}

		// if we don't get ids we have deleted them all
		if len(ids) == 0 {
			return deleted, nil
		}

		for _, id := range ids {
			if err := repo_service.DeleteRepositoryDirectly(ctx, adminUser, 0, id, true); err != nil {
				return deleted, err
			}
			deleted++
		}
	}
}

func init() {
	Register(&Check{
		Title:     "Deleted all content related to orphaned repos",
		Name:      "delete-orphaned-repos",
		IsDefault: false,
		Run:       handleDeleteOrphanedRepos,
		Priority:  4,
	})
}