package model

import (
	"github.com/tech/sendico/pkg/db/storable"
	"github.com/tech/sendico/pkg/mservice"
	"go.mongodb.org/mongo-driver/v2/bson"
	"golang.org/x/crypto/bcrypt"
)

type Filter int

type AccountStatus string

const (
	AccountPendingVerification AccountStatus = "pending_verification"
	AccountActive              AccountStatus = "active"
	AccountBlocked             AccountStatus = "blocked"
)

type AccountBase struct {
	storable.Base  `bson:",inline" json:",inline"`
	ArchivableBase `bson:",inline" json:",inline"`
	Describable    `bson:",inline" json:",inline"`
	LastName       string  `bson:"lastName" json:"lastName"`
	AvatarURL      *string `bson:"avatarUrl,omitempty" json:"avatarUrl,omitempty"`
}

func (*AccountBase) Collection() string {
	return mservice.Accounts
}

type AccountPublic struct {
	AccountBase  `bson:",inline" json:",inline"`
	UserDataBase `bson:",inline" json:",inline"`
}

type Account struct {
	AccountPublic `bson:",inline" json:",inline"`
	Password      string        `bson:"password" json:"-"` // password hash
	Status        AccountStatus `bson:"status" json:"-"`
}

func (a *Account) Copy() *Account {
	return &Account{
		AccountPublic: a.AccountPublic,
		Password:      a.Password,
		Status:        a.Status,
	}
}

func (a *Account) IsActive() bool {
	return a.Status == AccountActive
}

func (a *Account) HashPassword() error {
	key, err := bcrypt.GenerateFromPassword([]byte(a.Password), bcrypt.DefaultCost)
	if err == nil {
		a.Password = string(key)
	}

	return err
}

func (a *Account) MatchPassword(password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password))

	return err == nil
}

func AnonymousUserName(orgRef bson.ObjectID) string {
	return "anonymous@" + orgRef.Hex()
}

func AccountIsAnonymous(account *UserDataBase, orgRef bson.ObjectID) bool {
	if account == nil {
		return false
	}
	return AnonymousUserName(orgRef) == account.Login
}

type AccountBound interface {
	GetAccountRef() bson.ObjectID
}

type AccountBoundStorable interface {
	storable.Storable
	OrganizationBound
	GetAccountRef() *bson.ObjectID
}

const (
	AccountRefField = "accountRef"
)

type AccountBoundBase struct {
	storable.Base         `bson:",inline" json:",inline"`
	OrganizationBoundBase `bson:",inline" json:",inline"`
	AccountRef            *bson.ObjectID `bson:"accountRef,omitempty" json:"accountRef,omitempty"`
}

func (a *AccountBoundBase) GetAccountRef() *bson.ObjectID {
	return a.AccountRef
}
