// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

package sqlstore

import (
	"database/sql"
	"errors"
	"strconv"

	sq "github.com/Masterminds/squirrel"
	"github.com/mattermost/focalboard/server/model"
	"github.com/mattermost/focalboard/server/services/store"
)

var ErrInvalidCardLimitValue = errors.New("card limit value is invalid")

// activeCardsQuery applies the necessary filters to the query for it
// to fetch an active cards window if the cardLimit is set, or all the
// active cards if it's 0.
func (s *SQLStore) activeCardsQuery(builder sq.StatementBuilderType, selectStr string, cardLimit int) sq.SelectBuilder {
	query := builder.
		Select(selectStr).
		From(s.tablePrefix + "blocks b").
		Join(s.tablePrefix + "boards bd on b.board_id=bd.id").
		Where(sq.Eq{
			"b.delete_at":    0,
			"b.type":         model.TypeCard,
			"bd.is_template": false,
		})

	if cardLimit != 0 {
		query = query.
			Limit(1).
			Offset(uint64(cardLimit - 1))
	}

	return query
}

// getUsedCardsCount returns the amount of active cards in the server.
func (s *SQLStore) getUsedCardsCount(db sq.BaseRunner) (int, error) {
	row := s.activeCardsQuery(s.getQueryBuilder(db), "count(b.id)", 0).
		QueryRow()

	var usedCards int
	err := row.Scan(&usedCards)
	if err != nil {
		return 0, err
	}

	return usedCards, nil
}

// getCardLimitTimestamp returns the timestamp value from the
// system_settings table or zero if it doesn't exist.
func (s *SQLStore) getCardLimitTimestamp(db sq.BaseRunner) (int64, error) {
	scanner := s.getQueryBuilder(db).
		Select("value").
		From(s.tablePrefix + "system_settings").
		Where(sq.Eq{"id": store.CardLimitTimestampSystemKey}).
		QueryRow()

	var result string
	err := scanner.Scan(&result)
	if errors.Is(sql.ErrNoRows, err) {
		return 0, nil
	}
	if err != nil {
		return 0, err
	}

	cardLimitTimestamp, err := strconv.Atoi(result)
	if err != nil {
		return 0, ErrInvalidCardLimitValue
	}

	return int64(cardLimitTimestamp), nil
}

// updateCardLimitTimestamp updates the card limit value in the
// system_settings table with the timestamp of the nth last updated
// card, being nth the value of the cardLimit parameter. If cardLimit
// is zero, the timestamp will be set to zero.
func (s *SQLStore) updateCardLimitTimestamp(db sq.BaseRunner, cardLimit int) (int64, error) {
	query := s.getQueryBuilder(db).
		Insert(s.tablePrefix+"system_settings").
		Columns("id", "value")

	var value interface{} = 0
	if cardLimit != 0 {
		value = s.activeCardsQuery(sq.StatementBuilder, "b.update_at", cardLimit).
			OrderBy("b.update_at DESC").
			Prefix("COALESCE((").Suffix("), 0)")
	}
	query = query.Values(store.CardLimitTimestampSystemKey, value)

	if s.dbType == model.MysqlDBType {
		query = query.Suffix("ON DUPLICATE KEY UPDATE value = ?", value)
	} else {
		query = query.Suffix(
			`ON CONFLICT (id)
			 DO UPDATE SET value = EXCLUDED.value`,
		)
	}

	result, err := query.Exec()
	if err != nil {
		return 0, err
	}

	if _, err := result.RowsAffected(); err != nil {
		return 0, err
	}

	return s.getCardLimitTimestamp(db)
}
