/*
*
* Gosora MySQL Interface
* Copyright Azareal 2017 - 2020
*
 */
package install

import (
	"bytes"
	"database/sql"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/Azareal/Gosora/query_gen"
	_ "github.com/go-sql-driver/mysql"
)

//var dbCollation string = "utf8mb4_general_ci"

func init() {
	adapters["mysql"] = &MysqlInstaller{dbHost: ""}
}

type MysqlInstaller struct {
	db         *sql.DB
	dbHost     string
	dbUsername string
	dbPassword string
	dbName     string
	dbPort     string
}

func (ins *MysqlInstaller) SetConfig(dbHost string, dbUsername string, dbPassword string, dbName string, dbPort string) {
	ins.dbHost = dbHost
	ins.dbUsername = dbUsername
	ins.dbPassword = dbPassword
	ins.dbName = dbName
	ins.dbPort = dbPort
}

func (ins *MysqlInstaller) Name() string {
	return "mysql"
}

func (ins *MysqlInstaller) DefaultPort() string {
	return "3306"
}

func (ins *MysqlInstaller) dbExists(dbName string) (bool, error) {
	var waste string
	err := ins.db.QueryRow("SHOW DATABASES LIKE '" + dbName + "'").Scan(&waste)
	if err != nil && err != sql.ErrNoRows {
		return false, err
	} else if err == sql.ErrNoRows {
		return false, nil
	}
	return true, nil
}

func (ins *MysqlInstaller) InitDatabase() (err error) {
	_dbPassword := ins.dbPassword
	if _dbPassword != "" {
		_dbPassword = ":" + _dbPassword
	}
	db, err := sql.Open("mysql", ins.dbUsername+_dbPassword+"@tcp("+ins.dbHost+":"+ins.dbPort+")/")
	if err != nil {
		return err
	}

	// Make sure that the connection is alive..
	err = db.Ping()
	if err != nil {
		return err
	}
	fmt.Println("Successfully connected to the database")

	ins.db = db
	ok, err := ins.dbExists(ins.dbName)
	if err != nil {
		return err
	}

	if !ok {
		fmt.Println("Unable to find the database. Attempting to create it")
		_, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + ins.dbName)
		if err != nil {
			return err
		}
		fmt.Println("The database was successfully created")
	}

	/*fmt.Println("Switching to database ", ins.dbName)
	_, err = db.Exec("USE " + ins.dbName)
	if err != nil {
		return err
	}*/
	db.Close()

	db, err = sql.Open("mysql", ins.dbUsername+_dbPassword+"@tcp("+ins.dbHost+":"+ins.dbPort+")/"+ins.dbName)
	if err != nil {
		return err
	}

	// Make sure that the connection is alive..
	err = db.Ping()
	if err != nil {
		return err
	}
	fmt.Println("Successfully connected to the database")

	// Ready the query builder
	ins.db = db
	qgen.Builder.SetConn(db)
	return qgen.Builder.SetAdapter("mysql")
}

func (ins *MysqlInstaller) createTable(f os.FileInfo) error {
	table := strings.TrimPrefix(f.Name(), "query_")
	ext := filepath.Ext(table)
	if ext != ".sql" {
		return nil
	}
	table = strings.TrimSuffix(table, ext)

	// ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution
	q := "DROP TABLE IF EXISTS `" + table + "`;"
	_, err := ins.db.Exec(q)
	if err != nil {
		fmt.Println("Failed query:", q)
		fmt.Println("e:", err)
		return err
	}

	data, err := ioutil.ReadFile("./schema/mysql/" + f.Name())
	if err != nil {
		return err
	}
	data = bytes.TrimSpace(data)

	q = string(data)
	_, err = ins.db.Exec(q)
	if err != nil {
		fmt.Println("Failed query:", q)
		fmt.Println("e:", err)
		return err
	}
	fmt.Printf("Created table '%s'\n", table)

	return nil
}

func (ins *MysqlInstaller) TableDefs() (err error) {
	fmt.Println("Creating the tables")
	files, err := ioutil.ReadDir("./schema/mysql/")
	if err != nil {
		return err
	}

	_, err = ins.db.Exec("SET FOREIGN_KEY_CHECKS = 0;")
	if err != nil {
		return err
	}

	for _, f := range files {
		if !strings.HasPrefix(f.Name(), "query_") {
			continue
		}
		err := ins.createTable(f)
		if err != nil {
			return err
		}
	}

	_, err = ins.db.Exec("SET FOREIGN_KEY_CHECKS = 1;")
	return err
}

// ? - Moved this here since it was breaking the installer, we need to add this at some point
/* TODO: Implement the html-attribute setting type before deploying this */
/*INSERT INTO settings(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');*/

func (ins *MysqlInstaller) InitialData() error {
	fmt.Println("Seeding the tables")
	data, err := ioutil.ReadFile("./schema/mysql/inserts.sql")
	if err != nil {
		return err
	}
	data = bytes.TrimSpace(data)

	statements := bytes.Split(data, []byte(";"))
	for key, sBytes := range statements {
		statement := string(sBytes)
		if statement == "" {
			continue
		}
		statement += ";"

		fmt.Println("Executing query #" + strconv.Itoa(key) + " " + statement)
		_, err = ins.db.Exec(statement)
		if err != nil {
			return err
		}
	}
	return nil
}

func (ins *MysqlInstaller) CreateAdmin() error {
	return createAdmin()
}

func (ins *MysqlInstaller) DBHost() string {
	return ins.dbHost
}

func (ins *MysqlInstaller) DBUsername() string {
	return ins.dbUsername
}

func (ins *MysqlInstaller) DBPassword() string {
	return ins.dbPassword
}

func (ins *MysqlInstaller) DBName() string {
	return ins.dbName
}

func (ins *MysqlInstaller) DBPort() string {
	return ins.dbPort
}