package queries

import (
	"sync"

	"github.com/fiskerinc/cloud-services/pkg/db"
	"github.com/go-pg/pg/v10"
	"github.com/go-pg/pg/v10/orm"
	"github.com/pkg/errors"
)

type QueryBaseInterface interface {
	GetDBConn() *pg.DB
	GetClient() *db.DBClient
	SetClient(client *db.DBClient)
}

type QueryBase struct {
	client *db.DBClient
	once   sync.Once
}

func (q *QueryBase) GetDBConn() *pg.DB {
	return q.GetClient().GetConn()
}

func (q *QueryBase) GetClient() *db.DBClient {
	q.once.Do(func() {
		if q.client == nil {
			q.client = &db.DBClient{}
		}
	})

	return q.client
}

func (q *QueryBase) SetClient(client *db.DBClient) {
	if q.client != nil {
		q.client.Close()
	}
	q.client = client
}

// pageQuery update orm query with PageQueryOptions
func (q *QueryBase) pageQuery(query *orm.Query, options *PageQueryOptions) *orm.Query {
	if options == nil {
		return query
	}

	if options.Order != "" {
		query.Order(options.Order)
	}

	// A limit of 0 is not respected
	if options.Limit > 0 {
		query.Limit(options.Limit)
	}

	if options.Offset > 0 {
		query.Offset(options.Offset)
	}

	return query
}

func (q *QueryBase) countWithStack(count int, err error) (int, error) {
	return count, errors.WithStack(err)
}

func (q *QueryBase) resultWithStack(result orm.Result, err error) (orm.Result, error) {
	return result, errors.WithStack(err)
}

func (q *QueryBase) insertSelectWithStack(inserted bool, err error) (bool, error) {
	return inserted, errors.WithStack(err)
}

func (q *QueryBase) count(filter interface{}) (int, error) {
	return q.countWithStack(q.GetDBConn().Model(filter).Count())
}

func (q *QueryBase) delete(model interface{}) (orm.Result, error) {
	return q.resultWithStack(q.GetDBConn().Model(model).WherePK().Delete())
}

func (q *QueryBase) insert(model interface{}) (orm.Result, error) {
	return q.resultWithStack(q.GetDBConn().Model(model).Insert())
}

func (q *QueryBase) update(model interface{}) (orm.Result, error) {
	return q.resultWithStack(q.GetDBConn().Model(model).WherePK().Update())
}

func (q *QueryBase) hasErrorResult(total *ORMResults, result orm.Result, err error) bool {
	if err != nil {
		return true
	}

	total.AddResult(result)
	return false
}
