package entity

import (
	"time"

	"gorm.io/gorm"
)

// base
type (
	// uint64 ID for all tables
	ID uint64

	// base
	Base struct {
		ID        ID        `gorm:"primary_key;not null"`
		CreatedAt time.Time `gorm:"created_at;type:TIMESTAMP;not null;<-:create"` // create and read only
	}

	// Versioned provides the versioning for model, auto increment the version when update/save
	// foo := &Foo{}
	// 1. [don't support]batch save/create: db.Save([]*Foo{foo})
	// 2. [ok]save one: db.Save(foo)
	// 3. [ok]update one field: db.Model(foo).Update("a", 1)
	// 4. [ok]update multiple fields: db.Model(foo).Updates(map[string]interface{}{"a": 1, "b": 2})
	// 5. [ok]update by model: db.Model(foo}).Select("*").Updates(foo)
	Versioned struct {
		Versioning int `gorm:"versioning; default:1"`
	}

	// Updatable table
	Updatable struct {
		Base
		UpdatedAt *time.Time `gorm:"updated_at;type:TIMESTAMP"`
	}
)

type (
	IdAndVersioning interface {
		GetID() ID
		GetVersioning() int
	}
)

func (u *Base) GetID() ID {
	return u.ID
}

func (u *Versioned) GetVersioning() int {
	return u.Versioning
}

func (u *Versioned) BeforeUpdate(tx *gorm.DB) (err error) {
	// should go here if we use db.Save(model)/db.Updates(model)
	if u.Versioning != 0 {
		tx.Statement.SetColumn("versioning", u.Versioning+1)
	} else { // should go here if we use db.Update(k,v)/db.Updates(map)
		tx.Statement.SetColumn("versioning", gorm.Expr("versioning + 1"))
	}
	return nil
}

func (id ID) Uint64() uint64 {
	return uint64(id)
}
