// Copyright 2017 The OpenPitrix Authors. All rights reserved.
// Use of this source code is governed by a Apache license
// that can be found in the LICENSE file.

package db

import (
	"context"
	"sync"
	"time"

	"github.com/gocraft/dbr"

	"openpitrix.io/openpitrix/pkg/config"
	"openpitrix.io/openpitrix/pkg/logger"
)

type key int

var dbMap = sync.Map{}
var dbKey key

type Database struct {
	Conn *dbr.Connection
}

func OpenDatabase(cfg config.MysqlConfig) (*Database, error) {
	// https://github.com/go-sql-driver/mysql/issues/9
	conn, err := dbr.Open("mysql", cfg.GetUrl()+"?parseTime=1&multiStatements=1&charset=utf8mb4&collation=utf8mb4_unicode_ci", nil)
	if err != nil {
		return nil, err
	}
	conn.SetMaxIdleConns(100)
	conn.SetMaxOpenConns(100)
	conn.SetConnMaxLifetime(10 * time.Second)

	db := &Database{
		Conn: conn,
	}
	dbMap.Store(cfg, db)
	return db, nil
}

func NewContext(ctx context.Context, cfg config.MysqlConfig) context.Context {
	return context.WithValue(ctx, dbKey, cfg)
}

func FromContext(ctx context.Context) (*Database, bool) {
	cfg := ctx.Value(dbKey).(config.MysqlConfig)
	var err error
	db, ok := dbMap.Load(cfg)
	if !ok {
		db, err = OpenDatabase(cfg)
		if err != nil {
			logger.Critical(ctx, "Failed to open database: %+v", err)
			return nil, false
		}
	}
	return db.(*Database), true
}

func (db *Database) New(ctx context.Context) *Conn {
	actualDb, ok := FromContext(ctx)
	conn := db.Conn
	if ok {
		conn = actualDb.Conn
	}
	return &Conn{
		Session: conn.NewSession(&EventReceiver{ctx}),
		ctx:     ctx,
	}
}
