package controller

import (
    "fmt"
    "os"
    "time"

    "github.com/jmoiron/sqlx"
)

const DB_VERSION int = 4

func CheckDBVersionAndMigrate(db *sqlx.DB) {
    db.MustExec("CREATE SCHEMA IF NOT EXISTS arimelody")
    db.MustExec("SET search_path TO arimelody, public")
    db.MustExec(
        "CREATE TABLE IF NOT EXISTS arimelody.schema_version (" +
        "version INTEGER PRIMARY KEY, " +
        "applied_at TIMESTAMP DEFAULT current_timestamp)",
    )

    oldDBVersion := 0
    schemaVersionCount := 0
    err := db.Get(&schemaVersionCount, "SELECT COUNT(*) FROM schema_version")
    if err != nil { panic(err) }
    if schemaVersionCount > 0 {
        err := db.Get(&oldDBVersion, "SELECT MAX(version) FROM schema_version")
        if err != nil { panic(err) }
    }

    for oldDBVersion < DB_VERSION {
        switch oldDBVersion {
        case 0:
            // default case; assume no database exists
            ApplyMigration(db, "000-init")
            oldDBVersion = DB_VERSION

        case 1:
            // the irony is i actually have to awkwardly shove schema_version
            // into the old database in order for this to work LOL
            ApplyMigration(db, "001-pre-versioning")
            oldDBVersion = 2

        case 2:
            ApplyMigration(db, "002-audit-logs")
            oldDBVersion = 3

        case 3:
            ApplyMigration(db, "003-fail-lock")
            oldDBVersion = 4

        }
    }

    fmt.Printf("Database schema up to date.\n")
}

func ApplyMigration(db *sqlx.DB, scriptFile string) {
    fmt.Printf("Applying schema migration %s...\n", scriptFile)

    bytes, err := os.ReadFile("schema-migration/" + scriptFile + ".sql")
    if err != nil {
        fmt.Fprintf(os.Stderr, "FATAL: Failed to open schema file \"%s\": %v\n", scriptFile, err)
        os.Exit(1)
    }
    script := string(bytes)
    
    tx, err := db.Begin()
    if err != nil {
        fmt.Fprintf(os.Stderr, "FATAL: Failed to begin migration: %v\n", err)
        os.Exit(1)
    }

    _, err = tx.Exec(script)
    if err != nil {
        tx.Rollback()
        fmt.Fprintf(os.Stderr, "FATAL: Failed to apply migration: %v\n", err)
        os.Exit(1)
    }

    _, err = tx.Exec(
        "INSERT INTO schema_version (version, applied_at) " +
        "VALUES ($1, $2)",
        DB_VERSION,
        time.Now(),
    )
    if err != nil {
        tx.Rollback()
        fmt.Fprintf(os.Stderr, "FATAL: Failed to update schema version: %v\n", err)
        os.Exit(1)
    }

    err = tx.Commit()
    if err != nil {
        fmt.Fprintf(os.Stderr, "FATAL: Failed to commit transaction: %v\n", err)
        os.Exit(1)
    }
}
