package orm

import (
	"database/sql"
	"errors"
	"fmt"
	"reflect"

	"github.com/go-pg/pg/types"
)

type useQueryOne interface {
	useQueryOne() bool
}

type Model interface {
	ColumnScanner

	Init() error

	// NewModel returns ColumnScanner that is used to scan columns
	// from the current row.
	NewModel() ColumnScanner

	// AddModel adds ColumnScanner to the Collection.
	AddModel(ColumnScanner) error

	AfterQuery(DB) error
	AfterSelect(DB) error

	BeforeInsert(DB) error
	AfterInsert(DB) error

	BeforeUpdate(DB) error
	AfterUpdate(DB) error

	BeforeDelete(DB) error
	AfterDelete(DB) error
}

func NewModel(values ...interface{}) (Model, error) {
	if len(values) > 1 {
		return Scan(values...), nil
	}

	v0 := values[0]
	switch v0 := v0.(type) {
	case Model:
		return v0, nil
	case sql.Scanner:
		return Scan(v0), nil
	}

	v := reflect.ValueOf(v0)
	if !v.IsValid() {
		return nil, errors.New("pg: Model(nil)")
	}
	if v.Kind() != reflect.Ptr {
		return nil, fmt.Errorf("pg: Model(non-pointer %T)", v0)
	}
	v = v.Elem()

	switch v.Kind() {
	case reflect.Struct:
		return newStructTableModelValue(v), nil
	case reflect.Slice:
		typ := v.Type()
		structType := indirectType(typ.Elem())
		if structType.Kind() == reflect.Struct && structType != timeType {
			m := sliceTableModel{
				structTableModel: structTableModel{
					table: Tables.Get(structType),
					root:  v,
				},
				slice: v,
			}
			m.init(typ)
			return &m, nil
		} else {
			return &sliceModel{
				slice: v,
				scan:  types.Scanner(structType),
			}, nil
		}
	}

	return Scan(v0), nil
}
