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

package user

import (
	"context"
	"strconv"
)

const BootstrapAdminActorName = "GOD"

func getUserSettingInt64(ctx context.Context, userID int64, key string) (int64, bool, error) {
	value, err := GetUserSetting(ctx, userID, key)
	if err != nil {
		if IsErrUserSettingIsNotExist(err) {
			return 0, false, nil
		}
		return 0, false, err
	}
	if value == "" {
		return 0, false, nil
	}
	parsed, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return 0, false, err
	}
	return parsed, true, nil
}

func GetAdminGrantorID(ctx context.Context, userID int64) (int64, bool, error) {
	return getUserSettingInt64(ctx, userID, SettingsKeyAdminGrantedBy)
}

func GetSuperAdminGrantorID(ctx context.Context, userID int64) (int64, bool, error) {
	return getUserSettingInt64(ctx, userID, SettingsKeySuperAdminGrantedBy)
}

func HasBootstrapAdminGrant(ctx context.Context, userID int64) (bool, error) {
	for _, key := range []string{SettingsKeyAdminGrantedByName, SettingsKeySuperAdminGrantedByName} {
		value, err := GetUserSetting(ctx, userID, key)
		if err != nil {
			if IsErrUserSettingIsNotExist(err) {
				continue
			}
			return false, err
		}
		if value == BootstrapAdminActorName {
			return true, nil
		}
	}
	return false, nil
}

func IsGrantorOf(ctx context.Context, grantedUserID, possibleGrantorID int64) (bool, error) {
	for _, getter := range []func(context.Context, int64) (int64, bool, error){GetAdminGrantorID, GetSuperAdminGrantorID} {
		grantorID, has, err := getter(ctx, grantedUserID)
		if err != nil {
			return false, err
		}
		if has && grantorID == possibleGrantorID {
			return true, nil
		}
	}
	return false, nil
}

func IsEligibleAdminGrantor(ctx context.Context, userID int64) (bool, error) {
	u, err := GetUserByID(ctx, userID)
	if err != nil {
		if IsErrUserNotExist(err) {
			return false, nil
		}
		return false, err
	}
	return u.IsAdmin && u.IsActive && !u.ProhibitLogin, nil
}

func GetEffectiveAdminGrantorID(ctx context.Context, userID int64) (int64, bool, error) {
	visited := map[int64]struct{}{userID: {}}
	currentUserID := userID

	for {
		grantorID, has, err := GetAdminGrantorID(ctx, currentUserID)
		if err != nil {
			return 0, false, err
		}
		if !has {
			return 0, false, nil
		}
		if _, ok := visited[grantorID]; ok {
			return 0, false, nil
		}
		visited[grantorID] = struct{}{}

		eligible, err := IsEligibleAdminGrantor(ctx, grantorID)
		if err != nil {
			return 0, false, err
		}
		if eligible {
			return grantorID, true, nil
		}

		currentUserID = grantorID
	}
}
