// copied from one of my older projects
import { createPool } from 'mysql2/promise';

function getEnvVar(name) {
  const value = process.env[name];
  if (value == null) {
    throw new Error(`Environment variable ${name} is not defined`);
  }
  return value;
}

const pool = createPool({
  host: getEnvVar('MYSQL_HOST'),
  user: getEnvVar('MYSQL_USER'),
  password: getEnvVar('MYSQL_PASSWORD'),
  database: getEnvVar('MYSQL_DB'),
});

const migrations = [
  {
    name: "init",
    up: async (conn) => {
      await conn.query(`
        CREATE TABLE users (
          id INT PRIMARY KEY AUTO_INCREMENT,
          username VARCHAR(64) NOT NULL UNIQUE
        )
      `); // max length is double the actual max length cause of unicode stuff

      await conn.query(`
        CREATE TABLE games (
          id INT PRIMARY KEY AUTO_INCREMENT,
          winner INT,
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          FOREIGN KEY (winner) REFERENCES users(id)
        )
      `);

      await conn.query(`
        CREATE TABLE current_games (
          id INT PRIMARY KEY AUTO_INCREMENT,
          is_open BOOL,
          is_local BOOL,
          current_playing_user INT,
          turn_order JSON DEFAULT NULL,
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          FOREIGN KEY (current_playing_user) REFERENCES users(id)
        )
      `);

      await conn.query(`
        CREATE TABLE game_players (
          id INT PRIMARY KEY AUTO_INCREMENT,
          game INT,
          user INT,
          is_creator BOOL,
          FOREIGN KEY (game) REFERENCES current_games(id),
          FOREIGN KEY (user) REFERENCES users(id)
        )
      `)

      await conn.query(`
        CREATE TABLE turns (
          id INT PRIMARY KEY AUTO_INCREMENT,
          game INT,
          user INT,
          round_number INT,
          start_points INT,
          first_throw INT,
          second_throw INT,
          third_throw INT,
          end_points INT,
          FOREIGN KEY (game) REFERENCES current_games(id),
          FOREIGN KEY (user) REFERENCES users(id)
        )
      `)
    }
  },
  {
    name: "add_timestamp_to_turns",
    up: async (conn) => {
      await conn.query(`
        ALTER TABLE turns
          ADD COLUMN played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
        `
      )
    }
  },
  {
    name: "add_unique_constraint_game_players",
    up: async (conn) => {
      await conn.query(`
        ALTER TABLE game_players
        ADD CONSTRAINT uniq_game_user UNIQUE (game, user);
      `);
    }
  },
  {
    name: "merge_current_games_into_games",
    up: async (conn) => {
      await conn.query(`
        ALTER TABLE game_players
        DROP FOREIGN KEY game_players_ibfk_1;
      `);

      await conn.query(`
        ALTER TABLE turns
        DROP FOREIGN KEY turns_ibfk_1;
      `);

      await conn.query(`DROP TABLE IF EXISTS games;`);
      await conn.query(`ALTER TABLE current_games RENAME TO games;`);

      await conn.query(`
        ALTER TABLE games
        ADD COLUMN is_finished BOOL DEFAULT FALSE AFTER is_local,
        ADD COLUMN winner INT DEFAULT NULL AFTER is_finished,
        ADD CONSTRAINT fk_games_winner FOREIGN KEY (winner) REFERENCES users(id);
      `);

      await conn.query(`
        ALTER TABLE game_players
        ADD CONSTRAINT fk_game_players_game FOREIGN KEY (game) REFERENCES games(id);
      `);

      await conn.query(`
        ALTER TABLE turns
        ADD CONSTRAINT fk_turns_game FOREIGN KEY (game) REFERENCES games(id);
      `);
    }
  }
];

async function initDB() {
  await pool.query(`
    CREATE TABLE IF NOT EXISTS migrations (
      id INT PRIMARY KEY AUTO_INCREMENT,
      name VARCHAR(255) NOT NULL UNIQUE
    );
  `);

  for (const m of migrations) {
    const conn = await pool.getConnection();

    try {
      await conn.beginTransaction();

      const [rows] = await conn.query(
        `SELECT 1 FROM migrations WHERE name = ? LIMIT 1 FOR UPDATE`,
        [m.name]
      );

      if (rows.length === 0) {
        console.log(`Running migration: ${m.name}`);
        await m.up(conn);
        await conn.query(`INSERT INTO migrations (name) VALUES (?)`, [m.name]);
      }

      await conn.commit();
    } catch (err) {
      await conn.rollback();
      throw err;
    } finally {
      conn.release();
    }
  }
}

export {
  pool,
  initDB
};