package services

import (
	"errors"
	"fmt"
	"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
	"git.solsynth.dev/hypernet/passport/pkg/internal/database"
	"github.com/samber/lo"
	"gorm.io/gorm"
	"strconv"
)

func ListCommunityRealm() ([]models.Realm, error) {
	var realms []models.Realm
	if err := database.C.Where(&models.Realm{
		IsCommunity: true,
	}).Find(&realms).Error; err != nil {
		return realms, err
	}

	return realms, nil
}

func ListOwnedRealm(user models.Account) ([]models.Realm, error) {
	var realms []models.Realm
	if err := database.C.Where(&models.Realm{AccountID: user.ID}).Find(&realms).Error; err != nil {
		return realms, err
	}

	return realms, nil
}

func ListAvailableRealm(user models.Account) ([]models.Realm, error) {
	var realms []models.Realm
	var members []models.RealmMember
	if err := database.C.Where(&models.RealmMember{
		AccountID: user.ID,
	}).Find(&members).Error; err != nil {
		return realms, err
	}

	idx := lo.Map(members, func(item models.RealmMember, index int) uint {
		return item.RealmID
	})

	if err := database.C.Where("id IN ?", idx).Find(&realms).Error; err != nil {
		return realms, err
	}

	return realms, nil
}

func GetRealmWithAlias(alias string) (models.Realm, error) {
	tx := database.C.Where("alias = ?", alias)

	numericId, err := strconv.Atoi(alias)
	if err == nil {
		tx.Or("id = ?", numericId)
	}

	var realm models.Realm
	if err := tx.First(&realm).Error; err != nil {
		return realm, err
	}
	return realm, nil
}

func NewRealm(realm models.Realm, user models.Account) (models.Realm, error) {
	realm.Members = []models.RealmMember{
		{AccountID: user.ID, PowerLevel: 100},
	}

	err := database.C.Save(&realm).Error
	return realm, err
}

func CountRealmMember(realmId uint) (int64, error) {
	var count int64
	if err := database.C.Where(&models.RealmMember{
		RealmID: realmId,
	}).Model(&models.RealmMember{}).Count(&count).Error; err != nil {
		return 0, err
	} else {
		return count, nil
	}
}

func ListRealmMember(realmId uint, take int, offset int) ([]models.RealmMember, error) {
	var members []models.RealmMember

	if err := database.C.
		Limit(take).Offset(offset).
		Where(&models.RealmMember{RealmID: realmId}).
		Preload("Account").
		Find(&members).Error; err != nil {
		return members, err
	}

	return members, nil
}

func GetRealmMember(userId uint, realmId uint) (models.RealmMember, error) {
	var member models.RealmMember
	if err := database.C.Where(&models.RealmMember{
		AccountID: userId,
		RealmID:   realmId,
	}).Find(&member).Error; err != nil {
		return member, err
	}
	return member, nil
}

func AddRealmMember(user models.Account, affected models.Account, target models.Realm) error {
	var member models.RealmMember
	if err := database.C.Where(&models.RealmMember{
		AccountID: affected.ID,
		RealmID:   target.ID,
	}).First(&member).Error; err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {
		return fmt.Errorf("the user is already in the realm")
	}

	if !target.IsPublic && !target.IsCommunity {
		if member, err := GetRealmMember(user.ID, target.ID); err != nil {
			return fmt.Errorf("only realm member can add people: %v", err)
		} else if member.PowerLevel < 50 {
			return fmt.Errorf("only realm moderator can add member")
		}
		rel, err := GetRelationWithTwoNode(affected.ID, user.ID)
		if err != nil || HasPermNodeWithDefault(
			rel.PermNodes,
			"RealmAdd",
			true,
			rel.Status == models.RelationshipFriend,
		) {
			return fmt.Errorf("you unable to add this user to your realm")
		}
	}

	member = models.RealmMember{
		RealmID:   target.ID,
		AccountID: affected.ID,
	}
	err := database.C.Save(&member).Error
	return err
}

func RemoveRealmMember(user models.Account, affected models.RealmMember, target models.Realm) error {
	if user.ID != affected.AccountID {
		if member, err := GetRealmMember(user.ID, target.ID); err != nil {
			return fmt.Errorf("only realm member can remove other member: %v", err)
		} else if member.PowerLevel < 50 {
			return fmt.Errorf("only realm moderator can kick member")
		}
	}

	return database.C.Delete(&affected).Error
}

func EditRealm(realm models.Realm) (models.Realm, error) {
	err := database.C.Save(&realm).Error
	return realm, err
}

func DeleteRealm(realm models.Realm) error {
	return database.C.Delete(&realm).Error
}