using System;
using System.Collections.Generic;
using System.Linq;
using LinqToDB;
using LinqToDB.Data;
using trainav.web.database;
using trainav.web.database.Tables;

namespace trainav.web;

public static class Migrations {
	private const int DbVer = 2;

	private static readonly List<Migration> _migrations = new() {
		new Migration(1,
					  "CREATE TEMPORARY TABLE Tickets_backup(\"TicketID\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \"UserID\" INTEGER NOT NULL, \"OrderID\" TEXT NOT NULL UNIQUE, \"TicketInfo\" TEXT NOT NULL, \"TicketQR\" BLOB NOT NULL, \"TicketPkPass\" BLOB NOT NULL, \"TicketSecCode\" BLOB NOT NULL, \"Traveller\" TEXT)"),
		new Migration(1, "INSERT INTO Tickets_backup SELECT TicketID, UserID, OrderID, TicketInfo, TicketQR, TicketPkPass, TicketSecCode, Traveller FROM Tickets"),
		new Migration(1, "DROP Table Tickets"),
		new Migration(1,
					  "CREATE TABLE Tickets(\"TicketID\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \"UserID\" INTEGER NOT NULL, \"OrderID\" TEXT NOT NULL UNIQUE, \"TicketInfo\" TEXT NOT NULL, \"TicketQR\" BLOB NOT NULL, \"TicketPkPass\" BLOB, \"TicketSecCode\" BLOB NOT NULL, \"Traveller\" TEXT)"),
		new Migration(1, "INSERT INTO Tickets SELECT TicketID, UserID, OrderID, TicketInfo, TicketQR, TicketPkPass, TicketSecCode, Traveller FROM Tickets_backup"),
		new Migration(1, "DROP TABLE Tickets_backup"),
		new Migration(2,
					  "CREATE TEMPORARY TABLE Legs_backup(\"LegID\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \"TripID\" INTEGER NOT NULL, \"UserID\" INTEGER NOT NULL, \"TrainType\" TEXT NOT NULL, \"TrainNr\" INTEGER NOT NULL, \"DepStationID\" INTEGER NOT NULL, \"DepStation\" TEXT NOT NULL, \"DepTime\" TEXT NOT NULL, \"ArrStationID\" INTEGER NOT NULL, \"ArrStation\" TEXT NOT NULL, \"ArrTime\" TEXT NOT NULL, \"TicketID\" INTEGER, Comment TEXT)"),
		new Migration(2,
					  "INSERT INTO Legs_backup SELECT LegID, TripID, UserID, TrainType, TrainNr, DepStationID, DepStation, DepTime, ArrStationID, ArrStation, ArrTime, TicketID, Comment FROM Legs"),
		new Migration(2, "DROP Table Legs"),
		new Migration(2,
					  "CREATE TABLE Legs(\"LegID\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \"TripID\" INTEGER NOT NULL, \"UserID\" INTEGER NOT NULL, \"TrainType\" TEXT NOT NULL, \"TrainNr\" INTEGER NOT NULL, \"DepStationID\" INTEGER NOT NULL, \"DepStation\" TEXT NOT NULL, \"DepTime\" TEXT NOT NULL, \"ArrStationID\" INTEGER NOT NULL, \"ArrStation\" TEXT NOT NULL, \"ArrTime\" TEXT NOT NULL, Comment TEXT)"),
		new Migration(2, "INSERT INTO Legs SELECT LegID, TripID, UserID, TrainType, TrainNr, DepStationID, DepStation, DepTime, ArrStationID, ArrStation, ArrTime, Comment FROM Legs_backup"),
		new Migration(2,
					  "CREATE TEMPORARY TABLE Users_backup(\"UserID\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \"Username\" TEXT NOT NULL UNIQUE, \"Password\" TEXT NOT NULL)"),
		new Migration(2,
					  "INSERT INTO Users_backup SELECT UserID, Username, Password FROM Users"),
		new Migration(2, "DROP Table Users"),
		new Migration(2,
					  "CREATE TABLE Users(\"UserID\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \"Username\" TEXT NOT NULL UNIQUE)"),
		new Migration(2, "INSERT INTO Users SELECT UserID, Username FROM Users_backup"),
		new Migration(2, "DROP TABLE Users_backup"),
		new Migration(2, "DROP TABLE Legs_backup"),
		new Migration(2, "DROP TABLE Tickets"),
		new Migration(2, "DROP TABLE TicketLegs"),
		new Migration(2, "DROP TABLE Cards")
	};

	public static void RunMigrations() {
		using var db     = new Database.DbConn();
		var       ccolor = Console.ForegroundColor;

		if (!db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.Any()) {
			Console.ForegroundColor = ConsoleColor.DarkCyan;
			Console.Write("Running migration: ");
			Console.ForegroundColor = ConsoleColor.Yellow;
			Console.WriteLine("Initialize Database");

			db.CreateTable<DbInfo>();
			db.CreateTable<Leg>();
			db.CreateTable<Trip>();
			db.CreateTable<User>();

			db.InsertWithIdentity(new DbInfo { DbVer = DbVer });
		}
		else if (db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.All(t => t.TableName != "DbInfo")) {
			db.CreateTable<DbInfo>();
			db.InsertWithIdentity(new DbInfo { DbVer = 0 });
		}

		Console.ForegroundColor = ConsoleColor.Yellow;
		Console.WriteLine($"Database version: {db.DbInfo.ToList().First().DbVer}");

		var migrationsToRun = _migrations.FindAll(p => p.IntroducedWithDbVer > db.DbInfo.First().DbVer);
		if (migrationsToRun.Count == 0) {
			Console.ForegroundColor = ConsoleColor.Green;
			Console.WriteLine("No migrations to run.");
		}
		else {
			new Migration(0, "BEGIN TRANSACTION").Run(db);
			try {
				migrationsToRun.ForEach(p => p.Run(db));
			}
			catch {
				Console.ForegroundColor = ConsoleColor.DarkRed;
				Console.WriteLine($"Migrating to database version {DbVer} failed.");
				new Migration(0, "ROLLBACK TRANSACTION").Run(db);
				Console.ForegroundColor = ConsoleColor.DarkYellow;
				Console.WriteLine("Rolled back migrations.");
				Environment.Exit(1);
			}

			new Migration(0, "COMMIT TRANSACTION").Run(db);

			var newdb  = new Database.DbConn();
			var dbinfo = newdb.DbInfo.First();
			dbinfo.DbVer = DbVer;
			newdb.Update(dbinfo);

			Console.ForegroundColor = ConsoleColor.DarkGreen;
			Console.WriteLine($"Database version is now: {DbVer}");
			Console.ForegroundColor = ConsoleColor.Green;
			Console.WriteLine("Finished running migrations.");
		}

		Console.ForegroundColor = ccolor;
	}

	private class Migration {
		private readonly string _sql;
		public readonly  int    IntroducedWithDbVer;

		public Migration(int introducedWithDbVer, string sql) {
			IntroducedWithDbVer = introducedWithDbVer;
			_sql                = sql;
		}

		public void Run(DataConnection db) {
			Console.ForegroundColor = ConsoleColor.DarkCyan;
			Console.Write("Running migration: ");
			Console.ForegroundColor = ConsoleColor.Yellow;
			Console.WriteLine(_sql);
			db.Execute(_sql);
		}
	}
}
