package main

import (
	"database/sql"

	_ "github.com/mattn/go-sqlite3"
)

var migrations = []string{
	"ALTER TABLE Games ADD count INT DEFAULT 0;",
}

func connectDB() (*sql.DB, error) {
	db, err := sql.Open("sqlite3", "./game.db")
	if err != nil {
		return db, err
	}

	err = checkPreinstall(db)
	if err != nil {
		return db, err
	}

	return db, nil
}

func checkPreinstall(db *sql.DB) error {
	sqlStmt := `
        CREATE TABLE IF NOT EXISTS Games (id INTEGER NOT NULL PRIMARY KEY, game_flow TEXT NOT NULL, first_won BOOL);
        CREATE UNIQUE INDEX IF NOT EXISTS game_flow_idx ON Games (game_flow);

        CREATE TABLE IF NOT EXISTS Stats (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, count INT, comment TEXT);
        CREATE UNIQUE INDEX IF NOT EXISTS stats_idx ON Stats (name);
        INSERT OR IGNORE INTO Stats (name, count) VALUES ('total', 0);
        INSERT OR IGNORE INTO Stats (name, count) VALUES ('ai_won', 0);
        INSERT OR IGNORE INTO Stats (name, count) VALUES ('draw', 0);
        INSERT OR IGNORE INTO Stats (name, comment) VALUES ('last_ai_lost', NULL);
        INSERT OR IGNORE INTO Stats (name, count) VALUES ('last_migration', 0);
	`
	_, err := db.Exec(sqlStmt)
	if err != nil {
		return err
	}

	err = runMigrations(db)
	if err != nil {
		return err
	}

	return nil
}

func runMigrations(db *sql.DB) error {
	lastMigration, err := getStatsCounter(db, "last_migration")
	if err != nil {
		return err
	}

	for i := lastMigration; i < len(migrations); i++ {
		sqlStmt := migrations[i]
		_, err := db.Exec(sqlStmt)
		if err != nil {
			return err
		}

		db.Exec("UPDATE Stats SET count=count+1 WHERE name='last_migration';")
	}

	return nil
}

func getStatsCounter(db *sql.DB, name string) (int, error) {
	stmt, err := db.Prepare("SELECT count FROM Stats WHERE name = ?")
	if err != nil {
		return 0, err
	}
	defer stmt.Close()

	var count int
	err = stmt.QueryRow(name).Scan(&count)
	if err != nil {
		return 0, nil
	}

	return count, nil
}

func getStatsComment(db *sql.DB, name string) (string, error) {
	stmt, err := db.Prepare("SELECT comment FROM Stats WHERE name = ?")
	if err != nil {
		return "", err
	}
	defer stmt.Close()

	var comment string
	err = stmt.QueryRow(name).Scan(&comment)
	if err != nil {
		return "", nil
	}

	return comment, nil
}

func saveGameLog(db *sql.DB, game *Game) {
	db.Exec("UPDATE Stats SET count=count+1 WHERE name='total';")
	if game.hasWinner() {
		db.Exec(
			"INSERT OR IGNORE INTO Games (game_flow, first_won) VALUES (?, ?);",
			game.gameFlow,
			game.isFirstWon(),
		)
		db.Exec(
			"UPDATE Games SET count=count+1 WHERE game_flow=? AND first_won=?;",
			game.gameFlow,
			game.isFirstWon(),
		)

		if game.isAiWon() {
			db.Exec("UPDATE Stats SET count=count+1 WHERE name='ai_won';")
		} else {
			db.Exec("UPDATE Stats SET comment=datetime() WHERE name='last_ai_lost';")
		}
	} else {
		db.Exec("UPDATE Stats SET count=count+1 WHERE name='draw';")
	}
}

func getWinnerNextStep(db *sql.DB, game *Game) (string, bool) {
	stmt, err := db.Prepare(
		"SELECT game_flow FROM Games WHERE game_flow LIKE ? AND first_won = ? ORDER BY count DESC",
	)
	if err != nil {
		return "", false
	}
	defer stmt.Close()

	var gameFlow string
	err = stmt.QueryRow(game.gameFlow+"%", !game.aiSecond).Scan(&gameFlow)
	if err != nil {
		return "", false
	}

	return gameFlow[len(game.gameFlow) : len(game.gameFlow)+1],
		len(game.gameFlow)+1 == len(gameFlow)
}

func getOponentNextStepToWin(db *sql.DB, game *Game) string {
	stmt, err := db.Prepare(
		"SELECT game_flow FROM Games WHERE game_flow LIKE ? AND first_won = ? ORDER BY length(game_flow) ASC",
	)
	if err != nil {
		return ""
	}
	defer stmt.Close()

	var gameFlow string
	err = stmt.QueryRow(game.gameFlow+"%", game.aiSecond).Scan(&gameFlow)
	if err != nil {
		return ""
	}

	if len(game.gameFlow)+2 == len(gameFlow) {
		return gameFlow[len(game.gameFlow)+1 : len(game.gameFlow)+2]
	}

	return ""
}

func getStatsTable(db *sql.DB) [][]string {
	result := make([][]string, 0)
	rows, err := db.Query("SELECT id, name, IFNULL(count, ''), IFNULL(comment, '') FROM Stats")
	if err != nil {
		return result
	}

	defer rows.Close()
	for rows.Next() {
		var (
			id      string
			name    string
			count   string
			comment string
		)
		if err := rows.Scan(&id, &name, &count, &comment); err != nil {
			return result
		}
		result = append(result, []string{id, name, count, comment})
	}
	if err := rows.Err(); err != nil {
		return result
	}

	return result
}

func getGamesTable(db *sql.DB) [][]string {
	result := make([][]string, 0)
	rows, err := db.Query("SELECT * FROM Games ORDER BY count DESC")
	if err != nil {
		return result
	}

	defer rows.Close()
	for rows.Next() {
		var (
			id       string
			gameFlow string
			firstWon string
			count    string
		)
		if err := rows.Scan(&id, &gameFlow, &firstWon, &count); err != nil {
			return result
		}
		result = append(result, []string{id, gameFlow, firstWon, count})
	}
	if err := rows.Err(); err != nil {
		return result
	}

	return result
}