package fic

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/go-sql-driver/mysql"
)

// db stores the connection to the database
var db *sql.DB

// DSNGenerator returns DSN filed with values from environment
func DSNGenerator() string {
	db_user := "fic"
	db_password := "fic"
	db_host := ""
	db_db := "fic"

	if v, exists := os.LookupEnv("MYSQL_HOST"); exists {
		if strings.HasPrefix(v, "/") {
			db_host = "unix(" + v + ")"
		} else {
			db_host = "tcp(" + v + ":"
			if p, exists := os.LookupEnv("MYSQL_PORT"); exists {
				db_host += p + ")"
			} else {
				db_host += "3306)"
			}
		}
	}
	if v, exists := os.LookupEnv("MYSQL_PASSWORD"); exists {
		db_password = v
	} else if v, exists := os.LookupEnv("MYSQL_ROOT_PASSWORD"); exists {
		db_user = "root"
		db_password = v
	}
	if v, exists := os.LookupEnv("MYSQL_USER"); exists {
		db_user = v
	}
	if v, exists := os.LookupEnv("MYSQL_DATABASE"); exists {
		db_db = v
	}

	return db_user + ":" + db_password + "@" + db_host + "/" + db_db
}

func DBIsDuplicateKeyError(err error) bool {
	var mysqlErr *mysql.MySQLError
	return errors.As(err, &mysqlErr) && mysqlErr.Number == 1062
}

// DBInit establishes the connection to the database
func DBInit(dsn string) (err error) {
	if db, err = sql.Open("mysql", dsn+"?parseTime=true&foreign_key_checks=1"); err != nil {
		return
	}

	_, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';`)
	for i := 0; err != nil && i < 45; i += 1 {
		if _, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';`); err != nil && i <= 45 {
			log.Println("An error occurs when trying to connect to DB, will retry in 2 seconds: ", err)
			time.Sleep(2 * time.Second)
		}
	}

	return
}

// DBCreate creates all necessary tables used by the package
func DBCreate() error {
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS events(
  id_event INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  txt VARCHAR(255) NOT NULL,
  kind ENUM('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark') NOT NULL,
  time TIMESTAMP NOT NULL
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS themes(
  id_theme INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(255) NOT NULL,
  locked BOOLEAN NOT NULL DEFAULT 0,
  url_id VARCHAR(191) NOT NULL UNIQUE,
  path VARCHAR(191) NOT NULL UNIQUE,
  headline TEXT NOT NULL,
  intro TEXT NOT NULL,
  image VARCHAR(255) NOT NULL,
  authors TEXT NOT NULL,
  partner_img VARCHAR(255) NOT NULL,
  partner_href VARCHAR(255) NOT NULL,
  partner_text TEXT NOT NULL
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS teams(
  id_team INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(255) NOT NULL,
  color INTEGER NOT NULL,
  active BOOLEAN NOT NULL DEFAULT 1,
  external_id TEXT NOT NULL,
  password VARCHAR(255) NULL
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS certificates(
  id_cert BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  creation TIMESTAMP NOT NULL,
  password VARCHAR(255) NOT NULL,
  revoked TIMESTAMP NULL
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS team_members(
  id_member INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_team INTEGER,
  firstname VARCHAR(255) NOT NULL,
  lastname VARCHAR(255) NOT NULL,
  nickname VARCHAR(255) NOT NULL,
  company VARCHAR(255) NOT NULL,
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercices(
  id_exercice INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_theme INTEGER NOT NULL,
  title VARCHAR(255) NOT NULL,
  authors TEXT NOT NULL,
  image VARCHAR(255) NOT NULL,
  disabled BOOLEAN NOT NULL DEFAULT 0,
  headline TEXT NOT NULL,
  url_id VARCHAR(255) NOT NULL,
  path VARCHAR(191) NOT NULL UNIQUE,
  statement TEXT NOT NULL,
  overview TEXT NOT NULL,
  issue TEXT NOT NULL,
  issue_kind ENUM('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark') NOT NULL DEFAULT 'info',
  depend INTEGER,
  gain INTEGER NOT NULL,
  coefficient_cur FLOAT NOT NULL DEFAULT 1.0,
  finished TEXT NOT NULL,
  video_uri VARCHAR(255) NOT NULL,
  resolution MEDIUMTEXT NOT NULL,
  seealso TEXT NOT NULL,
  FOREIGN KEY(id_theme) REFERENCES themes(id_theme),
  FOREIGN KEY(depend) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_files(
  id_file INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  origin VARCHAR(255) NOT NULL,
  path VARCHAR(191) NOT NULL UNIQUE,
  id_exercice INTEGER NOT NULL,
  name VARCHAR(255) NOT NULL,
  cksum BINARY(64) NOT NULL,
  cksum_shown BINARY(64),
  size BIGINT UNSIGNED NOT NULL,
  disclaimer VARCHAR(255) NOT NULL,
  published BOOLEAN NOT NULL DEFAULT 1,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_hints(
  id_hint INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_exercice INTEGER NOT NULL,
  title VARCHAR(255) NOT NULL,
  content TEXT NOT NULL,
  cost INTEGER NOT NULL,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_flags(
  id_flag INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_exercice INTEGER NOT NULL,
  ordre TINYINT NOT NULL,
  label VARCHAR(255) NOT NULL,
  type VARCHAR(255) NOT NULL,
  placeholder VARCHAR(255) NOT NULL,
  help VARCHAR(255) NOT NULL,
  unit VARCHAR(255) NOT NULL,
  ignorecase BOOLEAN NOT NULL DEFAULT 0,
  notrim BOOLEAN NOT NULL DEFAULT 0,
  multiline BOOLEAN NOT NULL DEFAULT 0,
  validator_regexp VARCHAR(255) NULL,
  sort_re_grps BOOLEAN NOT NULL DEFAULT 0,
  cksum BINARY(64) NOT NULL,
  choices_cost MEDIUMINT NOT NULL,
  bonus_gain MEDIUMINT NOT NULL,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_flag_labels(
  id_label INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_exercice INTEGER NOT NULL,
  ordre TINYINT NOT NULL,
  label TEXT NOT NULL,
  variant VARCHAR(255) NOT NULL,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_hints_okey_deps(
  id_hint INTEGER NOT NULL,
  id_flag_dep INTEGER NOT NULL,
  FOREIGN KEY(id_hint) REFERENCES exercice_hints(id_hint),
  FOREIGN KEY(id_flag_dep) REFERENCES exercice_flags(id_flag)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_flags_deps(
  id_flag INTEGER NOT NULL,
  id_flag_dep INTEGER NOT NULL,
  FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag),
  FOREIGN KEY(id_flag_dep) REFERENCES exercice_flags(id_flag)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_flag_labels_deps(
  id_label INTEGER NOT NULL,
  id_flag_dep INTEGER NOT NULL,
  FOREIGN KEY(id_label) REFERENCES exercice_flag_labels(id_label),
  FOREIGN KEY(id_flag_dep) REFERENCES exercice_flags(id_flag)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS flag_choices(
  id_choice INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_flag INTEGER NOT NULL,
  label VARCHAR(255) NOT NULL,
  response VARCHAR(255) NOT NULL,
  FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_files_okey_deps(
  id_file INTEGER NOT NULL,
  id_flag INTEGER NOT NULL,
  FOREIGN KEY(id_file) REFERENCES exercice_files(id_file),
  FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_mcq(
  id_mcq INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_exercice INTEGER NOT NULL,
  ordre TINYINT NOT NULL,
  title VARCHAR(255) NOT NULL,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_files_omcq_deps(
  id_file INTEGER NOT NULL,
  id_mcq INTEGER NOT NULL,
  FOREIGN KEY(id_file) REFERENCES exercice_files(id_file),
  FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_hints_omcq_deps(
  id_hint INTEGER NOT NULL,
  id_mcq_dep INTEGER NOT NULL,
  FOREIGN KEY(id_hint) REFERENCES exercice_hints(id_hint),
  FOREIGN KEY(id_mcq_dep) REFERENCES exercice_mcq(id_mcq)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_mcq_okey_deps(
  id_mcq INTEGER NOT NULL,
  id_flag_dep INTEGER NOT NULL,
  FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq),
  FOREIGN KEY(id_flag_dep) REFERENCES exercice_flags(id_flag)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_flags_omcq_deps(
  id_flag INTEGER NOT NULL,
  id_mcq_dep INTEGER NOT NULL,
  FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag),
  FOREIGN KEY(id_mcq_dep) REFERENCES exercice_mcq(id_mcq)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_flag_labels_omcq_deps(
  id_label INTEGER NOT NULL,
  id_mcq_dep INTEGER NOT NULL,
  FOREIGN KEY(id_label) REFERENCES exercice_flag_labels(id_label),
  FOREIGN KEY(id_mcq_dep) REFERENCES exercice_mcq(id_mcq)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_mcq_omcq_deps(
  id_mcq INTEGER NOT NULL,
  id_mcq_dep INTEGER NOT NULL,
  FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq),
  FOREIGN KEY(id_mcq_dep) REFERENCES exercice_mcq(id_mcq)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS mcq_entries(
  id_mcq_entry INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_mcq INTEGER NOT NULL,
  label VARCHAR(255) NOT NULL,
  response BOOLEAN NOT NULL,
  FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS mcq_found(
  id_mcq INTEGER NOT NULL,
  id_team INTEGER NOT NULL,
  time TIMESTAMP NOT NULL,
  CONSTRAINT uq_found UNIQUE (id_mcq,id_team),
  FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS flag_found(
  id_flag INTEGER NOT NULL,
  id_team INTEGER NOT NULL,
  time TIMESTAMP NOT NULL,
  CONSTRAINT uc_found UNIQUE (id_flag,id_team),
  FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_solved(
  id_exercice INTEGER NOT NULL,
  id_team INTEGER NOT NULL,
  time TIMESTAMP NOT NULL,
  coefficient FLOAT NOT NULL DEFAULT 1.0,
  CONSTRAINT uc_solved UNIQUE (id_exercice,id_team),
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_tries(
  id_exercice INTEGER NOT NULL,
  id_team INTEGER NOT NULL,
  time TIMESTAMP NOT NULL,
  cksum BINARY(64) NOT NULL,
  nbdiff INTEGER NOT NULL DEFAULT 0,
  onegood BOOLEAN NOT NULL DEFAULT 0,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_tags(
  id_exercice INTEGER NOT NULL,
  tag VARCHAR(255) NOT NULL,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS team_hints(
  id_team INTEGER,
  id_hint INTEGER,
  time TIMESTAMP NOT NULL,
  coefficient FLOAT NOT NULL DEFAULT 1.0,
  CONSTRAINT uc_displayed UNIQUE (id_team,id_hint),
  FOREIGN KEY(id_hint) REFERENCES exercice_hints(id_hint),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS team_wchoices(
  id_team INTEGER,
  id_flag INTEGER,
  time TIMESTAMP NOT NULL,
  coefficient FLOAT NOT NULL DEFAULT 1.0,
  CONSTRAINT uc_displayed UNIQUE (id_team,id_flag),
  FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS claim_assignees(
  id_assignee INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(191) NOT NULL UNIQUE
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	db.Exec(`INSERT INTO claim_assignees VALUES (0, "$team");`)
	db.Exec(`UPDATE claim_assignees SET id_assignee = 0 WHERE name = "$team";`)
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS claims(
  id_claim INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  subject VARCHAR(255) NOT NULL,
  id_team INTEGER,
  id_exercice INTEGER,
  id_assignee INTEGER,
  creation TIMESTAMP NOT NULL,
  state ENUM('new', 'need-info', 'confirmed', 'in-progress', 'need-review', 'closed', 'invalid') NOT NULL,
  priority ENUM('low', 'medium', 'high', 'critical') NOT NULL,
  FOREIGN KEY(id_assignee) REFERENCES claim_assignees(id_assignee),
  FOREIGN KEY(id_team) REFERENCES teams(id_team),
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS claim_descriptions(
  id_description INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_claim INTEGER NOT NULL,
  id_assignee INTEGER,
  date TIMESTAMP NOT NULL,
  content TEXT NOT NULL,
  publish BOOLEAN NOT NULL DEFAULT 0,
  FOREIGN KEY(id_claim) REFERENCES claims(id_claim)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercices_qa(
  id_qa INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_exercice INTEGER NOT NULL,
  id_team INTEGER NULL,
  authuser VARCHAR(255) NOT NULL,
  subject VARCHAR(255) NOT NULL,
  creation TIMESTAMP NOT NULL,
  state VARCHAR(255) NOT NULL,
  solved TIMESTAMP NULL,
  closed TIMESTAMP NULL,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS qa_comments(
  id_comment INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_qa INTEGER NOT NULL,
  id_team INTEGER NULL,
  authuser VARCHAR(255) NOT NULL,
  date TIMESTAMP NOT NULL,
  content TEXT NOT NULL,
  FOREIGN KEY(id_qa) REFERENCES exercices_qa(id_qa),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS teams_qa_todo(
  id_todo INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_team INTEGER NOT NULL,
  id_exercice INTEGER NOT NULL,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS teams_qa_view(
  id_view INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
  id_team INTEGER NOT NULL,
  id_exercice INTEGER NOT NULL,
  FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
  FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
		return err
	}

	if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_distinct_tries AS SELECT id_exercice, id_team, MAX(time) AS time, cksum, nbdiff, MAX(onegood) AS onegood FROM exercice_tries GROUP BY id_team, id_exercice, cksum;"); err != nil {
		return err
	}
	if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_tries_notgood AS SELECT id_exercice, id_team, time, cksum, nbdiff, onegood FROM exercice_tries WHERE onegood = 0;"); err != nil {
		return err
	}
	if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_distinct_tries_notgood AS SELECT id_exercice, id_team, MAX(time) AS time, cksum, nbdiff, MAX(onegood) AS onegood FROM exercice_tries_notgood GROUP BY id_team, id_exercice, cksum;"); err != nil {
		return err
	}
	if err := DBRecreateDiscountedView(); err != nil {
		return err
	}

	return nil
}

func DBRecreateDiscountedView() (err error) {
	if db == nil {
		return
	}

	_, err = db.Exec("CREATE OR REPLACE VIEW exercices_discounted AS SELECT E.id_exercice, E.id_theme, E.title, E.authors, E.image, E.disabled, E.headline, E.url_id, E.path, E.statement, E.overview, E.issue, E.issue_kind, E.depend, E.gain - " + fmt.Sprintf("%f", DiscountedFactor) + " * E.gain * (COUNT(*) - 1) AS gain, E.coefficient_cur, E.finished, E.video_uri, E.resolution, E.seealso FROM exercices E LEFT OUTER JOIN exercice_solved S ON S.id_exercice = E.id_exercice GROUP BY E.id_exercice;")
	return
}

// DBClose closes the connection to the database
func DBClose() error {
	return db.Close()
}

func DBPrepare(query string) (*sql.Stmt, error) {
	return db.Prepare(query)
}

func DBQuery(query string, args ...interface{}) (*sql.Rows, error) {
	return db.Query(query, args...)
}

func DBExec(query string, args ...interface{}) (sql.Result, error) {
	return db.Exec(query, args...)
}

func DBQueryRow(query string, args ...interface{}) *sql.Row {
	return db.QueryRow(query, args...)
}
