/*
 * Copyright 2021 PingCAP, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package model

import (
	"time"

	"github.com/go-gormigrate/gormigrate/v2"
	// for mysql
	_ "gorm.io/driver/mysql"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)

func Dial(config *Config) (*gorm.DB, error) {
	config.FillWithDefaults()
	d, err := config.GetDialector()
	if err != nil {
		return nil, err
	}
	db, err := gorm.Open(d, &gorm.Config{
		Logger: gormlogger.Discard,
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
		},
	})
	if err != nil {
		return nil, err
	}
	connPool, err := db.DB()
	if err != nil {
		return nil, err
	}
	if config.MaxOpenConns > 0 {
		connPool.SetMaxOpenConns(config.MaxOpenConns)
	}
	if config.MaxIdleConns > 0 {
		connPool.SetMaxIdleConns(config.MaxIdleConns)
	}
	if config.MaxLifeConns > 0 {
		connPool.SetConnMaxLifetime(time.Duration(config.MaxLifeConns) * time.Second)
	}
	err = connPool.Ping()
	if err != nil {
		return nil, err
	}
	return db, nil
}

// Migrate do migration with versioning style instead of auto style.
func Migrate(db *gorm.DB) error {
	m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{
		{
			// create ingress table
			ID: "202108251400",
			Migrate: func(tx *gorm.DB) error {
				type Ingress struct {
					ID          uint   `gorm:"column:id;type:int(11) unsigned NOT NULL AUTO_INCREMENT;PRIMARY_KEY"`
					UserID      string `gorm:"column:user_id;type:varchar(64);uniqueIndex:ingress_type,priority:3;NOT NULL;DEFAULT:''"`
					Name        string `gorm:"column:name;type:varchar(64);uniqueIndex:ingress_type,priority:2;NOT NULL;DEFAULT:''"`
					Endpoint    string `gorm:"column:endpoint;type:varchar(512);NOT NULL;DEFAULT:''"`
					IngressType string `gorm:"column:ingress_type;type:enum('kafka','sqs');uniqueIndex:ingress_type,priority:1;NOT NULL;DEFAULT:'sqs'"`

					CreatedAt time.Time `gorm:"column:created_at;<-:false;type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP"`
					UpdatedAt time.Time `gorm:"column:updated_at;<-:false;type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"`
				}
				return tx.AutoMigrate(&Ingress{})
			},
			Rollback: func(tx *gorm.DB) error {
				return tx.Migrator().DropTable(IngressTableName)
			},
		},
		{
			ID: "2021-08-23",
			Migrate: func(tx *gorm.DB) error {
				type Sink struct {
					ID       uint     `gorm:"column:id;type:int(11) unsigned NOT NULL AUTO_INCREMENT;PRIMARY_KEY"`
					UserID   string   `gorm:"column:user_id;type:varchar(64);uniqueIndex:sink_user_id_sink_id_index,priority:2;NOT NULL;DEFAULT:''"`
					SinkID   string   `gorm:"column:sink_id;type:varchar(64);uniqueIndex:sink_user_id_sink_id_index,priority:1;NOT NULL;DEFAULT:''"`
					SinkType SinkType `gorm:"column:sink_type;type:tinyint(4);NOT NULL;DEFAULT:0"`
					Table    string   `gorm:"column:table;type:varchar(128);NOT NULL;DEFAULT:''"`
					Database string   `gorm:"column:database;type:varchar(128);NOT NULL;DEFAULT:''"`
					Schedule string   `gorm:"column:schedule;type:varchar(128);NOT NULL;DEFAULT:''"`

					// Disallow gorm to set the `created_at`
					CreatedAt time.Time `gorm:"column:created_at;<-:false;type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP"`
					UpdatedAt time.Time `gorm:"column:updated_at;<-:false;type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"`
				}
				return tx.AutoMigrate(&Sink{})
			},
			Rollback: func(tx *gorm.DB) error {
				return tx.Migrator().DropTable(SinkTableName)
			},
		},
		{
			ID: "202108300900",
			Migrate: func(tx *gorm.DB) error {
				type Job struct {
					IngressId string `gorm:"column:ingress_id;type:varchar(64);NOT NULL;DEFAULT:''"`
					SinkId    string `gorm:"column:sink_id;type:varchar(64);NOT NULL;DEFAULT:''"`
				}
				return tx.AutoMigrate(&Job{})
			},
			Rollback: func(tx *gorm.DB) error {
				if err := tx.Migrator().DropColumn(JobTableName, "ingress_id"); err != nil {
					return err
				}
				return tx.Migrator().DropColumn(JobTableName, "sink_id")

			},
		},
	})
	m.InitSchema(initSchema)
	return m.Migrate()
}
