package common

//import "log"
import (
	"database/sql"

	qgen "github.com/Azareal/Gosora/query_gen"
)

var Rstore ReplyStore

type ReplyStore interface {
	Get(id int) (*Reply, error)
	Each(f func(*Reply) error) error
	Exists(id int) bool
	Create(t *Topic, content, ip string, uid int) (id int, err error)
	Count() (count int)
	CountUser(uid int) (count int)
	CountMegaUser(uid int) (count int)
	CountBigUser(uid int) (count int)

	SetCache(cache ReplyCache)
	GetCache() ReplyCache
}

type SQLReplyStore struct {
	cache ReplyCache

	get           *sql.Stmt
	getAll        *sql.Stmt
	exists        *sql.Stmt
	create        *sql.Stmt
	count         *sql.Stmt
	countUser     *sql.Stmt
	countWordUser *sql.Stmt
}

func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore, error) {
	if cache == nil {
		cache = NewNullReplyCache()
	}
	re := "replies"
	return &SQLReplyStore{
		cache:         cache,
		get:           acc.Select(re).Columns("tid,content,createdBy,createdAt,lastEdit,lastEditBy,ip,likeCount,attachCount,actionType").Where("rid=?").Prepare(),
		getAll:        acc.Select(re).Columns("rid,tid,content,createdBy,createdAt,lastEdit,lastEditBy,ip,likeCount,attachCount,actionType").Prepare(),
		exists:        acc.Exists(re, "rid").Prepare(),
		create:        acc.Insert(re).Columns("tid,content,parsed_content,createdAt,lastUpdated,ip,words,createdBy").Fields("?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?").Prepare(),
		count:         acc.Count(re).Prepare(),
		countUser:     acc.Count(re).Where("createdBy=?").Prepare(),
		countWordUser: acc.Count(re).Where("createdBy=? AND words>=?").Prepare(),
	}, acc.FirstError()
}

func (s *SQLReplyStore) Get(id int) (*Reply, error) {
	r, err := s.cache.Get(id)
	if err == nil {
		return r, nil
	}

	r = &Reply{ID: id}
	err = s.get.QueryRow(id).Scan(&r.ParentID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.IP, &r.LikeCount, &r.AttachCount, &r.ActionType)
	if err == nil {
		_ = s.cache.Set(r)
	}
	return r, err
}

/*func (s *SQLReplyStore) eachr(f func(*sql.Rows) error) error {
	return eachall(s.getAll, f)
}*/

func (s *SQLReplyStore) Each(f func(*Reply) error) error {
	rows, err := s.getAll.Query()
	if err != nil {
		return err
	}
	defer rows.Close()
	for rows.Next() {
		r := new(Reply)
		if err := rows.Scan(&r.ID, &r.ParentID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.IP, &r.LikeCount, &r.AttachCount, &r.ActionType); err != nil {
			return err
		}
		if err := f(r); err != nil {
			return err
		}
	}
	return rows.Err()
}

func (s *SQLReplyStore) Exists(id int) bool {
	err := s.exists.QueryRow(id).Scan(&id)
	if err != nil && err != ErrNoRows {
		LogError(err)
	}
	return err != ErrNoRows
}

// TODO: Write a test for this
func (s *SQLReplyStore) Create(t *Topic, content, ip string, uid int) (id int, err error) {
	if Config.DisablePostIP {
		ip = ""
	}
	res, err := s.create.Exec(t.ID, content, ParseMessage(content, t.ParentID, "forums", nil, nil), ip, WordCount(content), uid)
	if err != nil {
		return 0, err
	}
	lastID, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}
	id = int(lastID)
	return id, t.AddReply(id, uid)
}

// TODO: Write a test for this
// Count returns the total number of topic replies on these forums
func (s *SQLReplyStore) Count() (count int) {
	return Countf(s.count)
}
func (s *SQLReplyStore) CountUser(uid int) (count int) {
	return Countf(s.countUser, uid)
}
func (s *SQLReplyStore) CountMegaUser(uid int) (count int) {
	return Countf(s.countWordUser, uid, SettingBox.Load().(SettingMap)["megapost_min_words"].(int))
}
func (s *SQLReplyStore) CountBigUser(uid int) (count int) {
	return Countf(s.countWordUser, uid, SettingBox.Load().(SettingMap)["bigpost_min_words"].(int))
}

func (s *SQLReplyStore) SetCache(cache ReplyCache) {
	s.cache = cache
}

func (s *SQLReplyStore) GetCache() ReplyCache {
	return s.cache
}