package dal

import (
	"context"
	"github.com/Pena-Co-Ltd/amocrm_templategen_back/dal/mongos"
	"github.com/Pena-Co-Ltd/amocrm_templategen_back/dal/postgres"
	"github.com/jackc/pgx/v4/pgxpool"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readpref"
	"go.uber.org/zap"
	"time"
)

type PostgresDAL struct {
	conn   *pgxpool.Pool
	User   *postgres.User
	YaDisk *postgres.YaDisk
}

func InitPostgresDAL(ctx context.Context, dbURL string) (*PostgresDAL, error) {
	conn, err := pgxpool.Connect(ctx, dbURL)
	if err != nil {
		return nil, err
	}

	dal := PostgresDAL{
		conn:   conn,
		User:   postgres.InitUser(ctx, conn),
		YaDisk: postgres.InitYaDisk(ctx, conn),
	}

	// Init all tables in data access layer
	//val := reflect.ValueOf(dal)
	//for i := 0; i < val.NumField(); i++ {
	//	result := val.Field(i).MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(ctx)})
	//
	//	if !result[0].IsNil() {
	//		log.Println(result[0])
	//	}
	//}

	return &dal, nil
}

func (dal *PostgresDAL) Disconnect() {

}

type MongoDAL struct {
	conn     *mongo.Client
	logger   *zap.Logger
	User     *mongos.User
	YaDisk   *mongos.YaDisk
	GDisk    *mongos.GDisk
	Amo      *mongos.Amo
	Template *mongos.Template
}

func InitMongoDAL(ctx context.Context, dbURL, dbTable string, logger *zap.Logger) (*MongoDAL, error) {
	conn, err := mongo.NewClient(options.Client().ApplyURI(dbURL))

	if err != nil {
		return nil, err
	}

	ctxTO, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	err = conn.Connect(ctxTO)

	if err != nil {
		return nil, err
	}

	dal := &MongoDAL{
		conn:     conn,
		logger:   logger,
		User:     mongos.InitUser(conn.Database(dbTable), logger),
		YaDisk:   mongos.InitYaDisk(conn.Database(dbTable), logger),
		GDisk:    mongos.InitGDisk(conn.Database(dbTable), logger),
		Amo:      mongos.InitAmo(conn.Database(dbTable), logger),
		Template: mongos.InitTemplate(conn.Database(dbTable), logger),
	}

	_, err = dal.Ping(ctx)

	if err == nil {
		logger.Info("InfoDalConnected")
	}

	return dal, err
}

func (dal *MongoDAL) Ping(ctx context.Context) (int64, error) {
	start := time.Now()
	err := dal.conn.Ping(ctx, readpref.Primary())
	delay := time.Since(start).Milliseconds()

	if err != nil {
		dal.logger.Error("ErrorDalPingFailed", zap.Error(err))
	} else {
		dal.logger.Info("InfoDalPing", zap.Int64("Delay", delay))
	}

	return delay, err
}

func (dal *MongoDAL) Disconnect() {
	err := dal.conn.Disconnect(context.TODO())

	if err != nil {
		dal.logger.Error("ErrorDalDisconnect", zap.Error(err))
	} else {
		dal.logger.Info("InfoDalDisconnected")
	}
}
