package models

import (
	"time"

	"code.nonshy.com/nonshy/website/pkg/log"
)

// Subscription table - for notifications. You comment on someone's post, you get subscribed
// to other comments added to the post (unless you opt off).
type Subscription struct {
	ID         uint64 `gorm:"primaryKey"`
	UserID     uint64 `gorm:"index"` // who it belongs to
	Subscribed bool   `gorm:"index"`
	TableName  string // on which of your tables (photos, comments, ...)
	TableID    uint64
	CreatedAt  time.Time
	UpdatedAt  time.Time
}

// GetSubscription looks for an existing subscription or returns error if not found.
func GetSubscription(user *User, tableName string, tableID uint64) (*Subscription, error) {
	var s *Subscription
	result := DB.Model(s).Where(
		"user_id = ? AND table_name = ? AND table_id = ?",
		user.ID, tableName, tableID,
	).First(&s)
	return s, result.Error
}

// CountSubscriptions counts how many comment threads the user is subscribed to.
func CountSubscriptions(user *User) int64 {
	var (
		count  int64
		result = DB.Model(&Subscription{}).Where(
			"user_id = ? AND subscribed IS TRUE",
			user.ID,
		).Count(&count)
	)
	if result.Error != nil {
		log.Error("Error in CountSubscriptions(%s): %s", user.Username, result.Error)
	}
	return count
}

// UnsubscribeAllThreads removes subscription preferences for all comment threads.
func UnsubscribeAllThreads(user *User) error {
	return DB.Where(
		"user_id = ?",
		user.ID,
	).Delete(&Subscription{}).Error
}

// GetSubscribers returns all of the UserIDs that are subscribed to a thread.
func GetSubscribers(tableName string, tableID uint64) []uint64 {
	var userIDs = []uint64{}
	result := DB.Table(
		"subscriptions",
	).Select(
		"user_id",
	).Where(
		"table_name = ? AND table_id = ? AND subscribed IS TRUE",
		tableName, tableID,
	).Scan(&userIDs)

	if result.Error != nil {
		log.Error("GetSubscribers(%s, %d): couldn't get user IDs: %s", tableName, tableID, result.Error)
	}

	return userIDs
}

// IsSubscribed checks whether a user is currently subscribed (and notified) to a thing.
// Returns whether the row exists, and whether the user is to be notified (false if opted out).
func IsSubscribed(user *User, tableName string, tableID uint64) (exists bool, notified bool) {
	if sub, err := GetSubscription(user, tableName, tableID); err != nil {
		return false, false
	} else {
		return true, sub.Subscribed
	}
}

// SubscribeTo creates a subscription to a thing (comment thread) to be notified of future activity on.
func SubscribeTo(user *User, tableName string, tableID uint64) (*Subscription, error) {
	// Is there already a subscription row?
	if sub, err := GetSubscription(user, tableName, tableID); err == nil {
		return sub, err
	}

	// Create the default subscription.
	sub := &Subscription{
		UserID:     user.ID,
		Subscribed: true,
		TableName:  tableName,
		TableID:    tableID,
	}
	result := DB.Create(sub)
	return sub, result.Error
}

// Save a subscription.
func (n *Subscription) Save() error {
	return DB.Save(n).Error
}
