package models

import (
	"time"

	"gorm.io/gorm"
)

// Block table.
type Block struct {
	ID           uint64 `gorm:"primaryKey"`
	SourceUserID uint64 `gorm:"index"`
	TargetUserID uint64 `gorm:"index"`
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

// AddBlock is sourceUserId adding targetUserId to their block list.
func AddBlock(sourceUserID, targetUserID uint64) error {
	// Unfriend in the process.
	RemoveFriend(sourceUserID, targetUserID)

	// Did we already block this user?
	var b *Block
	forward := DB.Where(
		"source_user_id = ? AND target_user_id = ?",
		sourceUserID, targetUserID,
	).First(&b).Error

	// Update existing.
	if forward == nil {
		return nil
	}

	// Create the block.
	b = &Block{
		SourceUserID: sourceUserID,
		TargetUserID: targetUserID,
	}
	return DB.Create(b).Error
}

// IsBlocking quickly sees if either user blocks the other.
func IsBlocking(sourceUserID, targetUserID uint64) bool {
	b := &Block{}
	result := DB.Where(
		"(source_user_id = ? AND target_user_id = ?) OR "+
			"(target_user_id = ? AND source_user_id = ?)",
		sourceUserID, targetUserID,
		sourceUserID, targetUserID,
	).First(&b)
	return result.Error == nil
}

// IsBlocked quickly checks if sourceUserID currently blocks targetUserID.
func IsBlocked(sourceUserID, targetUserID uint64) bool {
	b := &Block{}
	result := DB.Where(
		"source_user_id = ? AND target_user_id = ?",
		sourceUserID, targetUserID,
	).First(&b)
	return result.Error == nil
}

// PaginateBlockList views a user's blocklist.
func PaginateBlockList(userID uint64, pager *Pagination) ([]*User, error) {
	// We paginate over the Block table.
	var (
		bs      = []*Block{}
		userIDs = []uint64{}
		query   *gorm.DB
	)

	query = DB.Where(
		"source_user_id = ?",
		userID,
	)

	query = query.Order(pager.Sort)
	query.Model(&Block{}).Count(&pager.Total)
	result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&bs)
	if result.Error != nil {
		return nil, result.Error
	}

	// Now of these friends get their User objects.
	for _, b := range bs {
		userIDs = append(userIDs, b.TargetUserID)
	}

	return GetUsers(userIDs)
}

// BlockedUserIDs returns all user IDs blocked by the user.
func BlockedUserIDs(userId uint64) []uint64 {
	var (
		bs      = []*Block{}
		userIDs = []uint64{}
	)
	DB.Where("source_user_id = ? OR target_user_id = ?", userId, userId).Find(&bs)
	for _, row := range bs {
		for _, uid := range []uint64{row.TargetUserID, row.SourceUserID} {
			if uid != userId {
				userIDs = append(userIDs, uid)
			}
		}
	}
	return userIDs
}

// UnblockUser removes targetUserID from your blocklist.
func UnblockUser(sourceUserID, targetUserID uint64) error {
	result := DB.Where(
		"source_user_id = ? AND target_user_id = ?",
		sourceUserID, targetUserID,
	).Delete(&Block{})
	return result.Error
}

// Save photo.
func (b *Block) Save() error {
	result := DB.Save(b)
	return result.Error
}
