package contracts

import (
	"reflect"

	"git.fsdpf.net/go/db"
)

type OrmExecute int

type RelationType string

type QueryDataType string
type ConditionType string
type ConditionOperator string
type ConditionTokenType string
type OrderByDirection string

const (
	OrmExecute_Query OrmExecute = iota
	OrmExecute_Show
	OrmExecute_Store
	OrmExecute_Destroy
)

const (
	RelationType_HasOne  RelationType = "hasOne"
	RelationType_HasMany RelationType = "hasMany"
	RelationType_Inner   RelationType = "inner"
	RelationType_Left    RelationType = "left"
	RelationType_Right   RelationType = "right"
)

const (
	ConditionType_OR  ConditionType = "OR"
	ConditionType_AND ConditionType = "AND"
)

const (
	ConditionTokenType_SQL    ConditionTokenType = "sql"
	ConditionTokenType_FUNC   ConditionTokenType = "func"
	ConditionTokenType_PARAM  ConditionTokenType = "param"
	ConditionTokenType_STRING ConditionTokenType = "string"
)

const (
	ConditionOperator_IS_NULL     ConditionOperator = "IS NULL"
	ConditionOperator_IS_NOT_NULL ConditionOperator = "IS NOT NULL"
	ConditionOperator_EQ          ConditionOperator = "="
	ConditionOperator_NE          ConditionOperator = "!="
	ConditionOperator_GT          ConditionOperator = ">"
	ConditionOperator_GE          ConditionOperator = ">="
	ConditionOperator_LT          ConditionOperator = "<"
	ConditionOperator_LE          ConditionOperator = "<="
	ConditionOperator_LIKE        ConditionOperator = "LIKE"
	ConditionOperator_NOT_LIKE    ConditionOperator = "NOT LIKE"
	ConditionOperator_IN          ConditionOperator = "IN"
	ConditionOperator_NOT_IN      ConditionOperator = "NOT IN"
	ConditionOperator_REGEXP      ConditionOperator = "REGEXP"
	ConditionOperator_NOT_REGEXP  ConditionOperator = "NOT REGEXP"
)

const (
	QueryDataType_Number  QueryDataType = "number"
	QueryDataType_Integer QueryDataType = "integer"
	QueryDataType_Float   QueryDataType = "float"
	QueryDataType_String  QueryDataType = "string"
	QueryDataType_Bool    QueryDataType = "bool"
	QueryDataType_Array   QueryDataType = "array"
	QueryDataType_Json    QueryDataType = "json"
)

const (
	OrderByDirection_ASC  OrderByDirection = db.ORDER_ASC
	OrderByDirection_DESC OrderByDirection = db.ORDER_DESC
)

const (
	QueryField_IsExpr = 1 << iota
	QueryField_IsOmitempty
	QueryField_Ignored
)

type Orm interface {
	SetGlobalParams(g GlobalParams) Orm
	GetModel(params ...string) Model
	SetRelationModel(m Model) Model
	SetQueryField(qf QueryField) error
	SetOrderBy(params []OrderBy) error
	SetCondition(cond Condition) error
	SetController(ctr reflect.Value) error
	Execute(OrmExecute) (any, error)
	Clone(tx *db.Transaction) Orm
	Count() int
	// Exists() bool
}
type Relation interface {
	Parent() Model
	AppendTo(Model)
	Type() RelationType
	SetType(RelationType)
	SetCondition(Condition) Condition
	SetPrependCondition(Condition) Condition
	GetCondition() Condition
	GetRelationResource() string
	GetRelationField() string
	GetRelationForeignKey() string
}

type Join interface {
	Relation
	GetCode() string
	GetResource() Resource
	// GetDependencies(items []ResRelation, dependencies ...string) []string
	Inject(dbBuilder *db.Builder, m Model)
}
type Model interface {
	Relation
	GetCode() string
	GetPrimaryKey() string
	GetParam(k string) GlobalParams
	GetParamWithPrefix(k string, prefix string) GlobalParams
	GetGlobalParamsUser() User
	GetQueryFieldsStruct(extends ...reflect.StructField) any
	GetQueryFieldsSliceStruct(fields ...reflect.StructField) any
	GetAttribute() map[string]any
	GetResult() reflect.Value
	GetResource() Resource
	QueryBuilder() *db.Builder
	GetTransaction() *db.Transaction
	SetQueryField(QueryField)
	SetRelationResult(code string, vItems reflect.Value)
	GetJoinsCode() []string
	SetAuthDB(ResAuthDB)
	SetOrderBy(params ...OrderBy) Model
	SetGroupBy(params ...GroupBy) Model
	SetJoin(Join) Model
	SetLimit(int) Model
	SetOffset(int) Model
	SetAttribute(data any) Model
	SetPrimaryKey(string) Model
	SetWithRecursive(QueryWithRecursive) Model
	DelQueryField(string)
	Query() error
	Store() error
	Destroy() error
	Count() (int, error)
	// Exists() (bool, error)
}

type Condition interface {
	Type() ConditionType
	IsEmpty() bool
	IsNotEmpty() bool
	ToSql(Model) db.Expression
	AppendTo(Condition)
	SetExpr(ConditionExpr) Condition
	SetCondition(Condition) Condition
	SetMatchPrefix(string) Condition
	GetFields(operator ConditionOperator, types ...ConditionTokenType) map[string]string
	GetFieldsValue(m Model, isWithResource bool) map[string]any
}

type ConditionExpr interface {
	GetField() string
	GetFieldResource() string
	GetOperator() ConditionOperator

	SetMatchPrefix(string)

	AppendTo(Condition)
	ToSql(m Model) db.Expression
	GetTokenName() string
	GetTokenType() ConditionTokenType
	GetTokenValue(Model) any
	GetTokenSqlValue(Model) string
	IsIgnoreEmptyParma(Model) bool
}

type QueryField interface {
	ResField
	Type() QueryDataType
	Alias() string
	GetCodeOrAlias() string
	IsExpr() bool
	IsOmitempty() bool
	Ignored() bool
	SetOptions(int) QueryField
	ToSql() db.Expression
	ToStructField(tags ...string) reflect.StructField
}

type OrderBy interface {
	ToSql() db.Expression
	Inject(dbBuilder *db.Builder, m Model)
}

type GroupBy interface {
	ToSql() db.Expression
	Inject(dbBuilder *db.Builder, m Model)
}

type QueryWithRecursive interface {
	ToQueryBuilder(*db.Builder, Model) (*db.Builder, any)
	ToTreeData(any) any
}