package models

import (
	"strings"
	"time"
)

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

// GetMessage by ID.
func GetMessage(id uint64) (*Message, error) {
	m := &Message{}
	result := DB.First(&m, id)
	return m, result.Error
}

// GetMessages for a user.
func GetMessages(userID uint64, sent bool, pager *Pagination) ([]*Message, error) {
	var (
		m              = []*Message{}
		blockedUserIDs = BlockedUserIDs(userID)
		where          = []string{}
		placeholders   = []interface{}{}
	)

	if sent {
		where = append(where, "source_user_id = ?")
		placeholders = append(placeholders, userID)

		if len(blockedUserIDs) > 0 {
			where = append(where, "target_user_id NOT IN ?")
			placeholders = append(placeholders, blockedUserIDs)
		}
	} else {
		where = append(where, "target_user_id = ?")
		placeholders = append(placeholders, userID)

		if len(blockedUserIDs) > 0 {
			where = append(where, "source_user_id NOT IN ?")
			placeholders = append(placeholders, blockedUserIDs)
		}
	}

	query := DB.Where(
		strings.Join(where, " AND "),
		placeholders...,
	).Order(pager.Sort)

	query.Model(&Message{}).Count(&pager.Total)
	result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&m)
	return m, result.Error
}

// GetMessageThread returns paginated message history between two people.
func GetMessageThread(sourceUserID, targetUserID uint64, pager *Pagination) ([]*Message, error) {
	var m = []*Message{}

	query := DB.Where(
		"(source_user_id = ? AND target_user_id = ?) OR (source_user_id = ? AND target_user_id = ?)",
		sourceUserID, targetUserID,
		targetUserID, sourceUserID,
	).Order(pager.Sort)

	query.Model(&Message{}).Count(&pager.Total)
	result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&m)
	return m, result.Error
}

// CountUnreadMessages gets the count of unread messages for a user.
func CountUnreadMessages(userID uint64) (int64, error) {
	query := DB.Where(
		"target_user_id = ? AND read = ?",
		userID,
		false,
	)

	var count int64
	result := query.Model(&Message{}).Count(&count)
	return count, result.Error
}

// SendMessage from a source to a target user.
func SendMessage(sourceUserID, targetUserID uint64, message string) (*Message, error) {
	m := &Message{
		SourceUserID: sourceUserID,
		TargetUserID: targetUserID,
		Message:      message,
		Read:         false,
	}

	result := DB.Create(m)
	return m, result.Error
}

// Save message.
func (m *Message) Save() error {
	result := DB.Save(m)
	return result.Error
}
