using System;
using System.Data.SQLite;

namespace BulkPrinting
{
    class Migrations
    {
        private delegate void PostMigrateDelegate(DBHelper db, SQLiteTransaction trans);

        public static void CheckMigrations(DBHelper db)
        {
            var result = db.ExecuteScalar("SELECT Value FROM Parameters WHERE Key = 'Migration'");
            int startingMigration;
            if (result == null)
            {
                startingMigration = 0;
            }
            else if (!int.TryParse((string)result, out startingMigration))
            {
                throw new Exception(String.Format("Invalid migration value: {0}", result));
            }

            if (startingMigration > 5)
            {
                throw new Exception("Database is for a newer version of this application. Please upgrade and try again.");
            }
            if (startingMigration < 1)
            {
                Migration1(db);
            }
            if (startingMigration < 2)
            {
                Migration2(db);
            }
            if (startingMigration < 3)
            {
                Migration3(db);
            }
            if (startingMigration < 4)
            {
                Migration4(db);
            }
            if (startingMigration < 5)
            {
                Migration5(db);
            }
            //Add further migration executions here - Migration = '1', '2', '3' etc

            return;
        }

        private static bool ApplyMigrationQueries(DBHelper db, PostMigrateDelegate postMigrate, params string[] queries)
        {
            lock (db.WriteLock)
            {
                using (var trans = db.BeginTransaction())
                {
                    foreach (var query in queries)
                    {
                        using (var command = db.CreateCommand(query, trans))
                        {
                            command.ExecuteNonQuery();
                        }
                    }
                    postMigrate?.Invoke(db, trans);
                    trans.Commit();
                }
            }
            return true;
        }

        public static bool InitialVersion(DBHelper db)
        {
            return ApplyMigrationQueries(
                db, null,
                "CREATE TABLE Batch (" +
                    "Id INT," +
                    "OrderDate DATETIME," +
                    "OrderGuid VARCHAR(36), " +
                    "OrderReference VARCHAR(20) NOT NULL," +
                    "NetworkId INT," +
                    "NetworkName VARCHAR(20) NOT NULL," +
                    "ProductId INT," +
                    "ProductDescription VARCHAR(50)," +
                    "VoucherType INT," +
                    "FaceValue DECIMAL(10,2)," +
                    "DiscountPercentage DECIMAL(10,2)," +
                    "RequestedQuantity INT," +
                    "DeliveredQuantity INT," +
                    "Cost DECIMAL(10,2)," +
                    "ReadyForDownload BOOLEAN)",

                "CREATE TABLE Voucher(" +
                    "Id INT ," +
                    "SequenceNumber INT," +
                    "ExpiryDate DATE," +
                    "Serial VARCHAR(50)," +
                    "EncryptedPIN VARCHAR(50)," +
                    "BatchId INT)",

                "CREATE TABLE SessionData(" +
                    "Key VARCHAR(20)," +
                    "Value TEXT)",

                "CREATE TABLE Parameters(" +
                    "Key VARCHAR(20)," +
                    "Value TEXT)",

                "CREATE TABLE AccessControlTracking (" +
                    "UserID INT NOT NULL," +
                    "Permission VARCHAR(20)," +
                    "Date DATETIME," +
                    "CurrentUsage DECIMAL)",

                "CREATE TABLE Logs(" +
                    "Id INTEGER PRIMARY KEY," +
                    "UserId INT NOT NULL," +
                    "VoucherId INT," +
                    "EventDate DATETIME," +
                    "EventType VARCHAR(50)," +
                    "Retry BOOLEAN)",

                "CREATE TABLE Orders(" +
                    "Id INTEGER PRIMARY KEY," +
                    "UserId INT NOT NULL," +
                    "UserName VARCHAR(50)," +
                    "OrderDate DATETIME," +
                    "OrderCost DECIMAL," +
                    "BalanceAfterOrder DECIMAL)",

                "CREATE TABLE OrderedItems(" +
                    "Id INTEGER PRIMARY KEY," +
                    "OrderId INT NOT NULL," +
                    "Cost DECIMAL," +
                    "Quantity INT," +
                    "Description VARCHAR(50)," +
                    "ProductId)"
            );
        }

        public static bool Migration1(DBHelper db)
        {
            return ApplyMigrationQueries(
                db, null,
                "ALTER TABLE Orders ADD COLUMN InternalReference VARCHAR(32)",
                "ALTER TABLE Batch ADD COLUMN InternalReference VARCHAR(32)",

                // IMPORTANT
                "INSERT INTO Parameters (Key, Value) VALUES ('Migration','1')"
            );
        }

        public static bool Migration2(DBHelper db)
        {
            return ApplyMigrationQueries(
                db, null,
                "ALTER TABLE Batch ADD COLUMN Downloaded BOOLEAN",
                "CREATE UNIQUE INDEX Batch_Id ON Batch(Id)",
                "CREATE INDEX Batch_OrderDate ON Batch(OrderDate)",
                "CREATE INDEX Batch_Downloaded_Id ON Batch(Downloaded, Id)",
                "CREATE UNIQUE INDEX Voucher_Id ON Voucher(Id)",
                "CREATE UNIQUE INDEX Voucher_BatchId_SequenceNumber ON Voucher(BatchId, SequenceNumber)",
                "CREATE UNIQUE INDEX SessionData_Key ON SessionData(Key)",
                "CREATE UNIQUE INDEX Parameters_Key ON Parameters(Key)",
                "CREATE INDEX Orders_OrderDate ON Orders(OrderDate)",
                "CREATE INDEX OrderedItems_OrderId ON OrderedItems(OrderId)",
                "UPDATE Batch SET Downloaded=(ReadyForDownload=1 AND DeliveredQuantity<=COALESCE((SELECT COUNT(*) FROM Voucher WHERE Voucher.BatchId=Batch.Id), 0))",
                "INSERT INTO Parameters (Key, Value) VALUES ('LastSyncedBatchId', '0')",
                "INSERT INTO Parameters (Key, Value) VALUES ('SyncBackwardsFromLogId', '0')",
                "INSERT INTO Parameters (Key, Value) VALUES ('LoggingInitialised', '" + false.ToString() + "')",
                "CREATE TABLE Logs_New (" +
                    "Id INTEGER PRIMARY KEY AUTOINCREMENT," +
                    "UserId INT NOT NULL," +
                    "VoucherId INT," +
                    "EventDate DATETIME," +
                    "EventType VARCHAR(50)," +
                    "Retry BOOLEAN)",
                "INSERT INTO Logs_New (Id, UserId, VoucherId, EventDate, EventType, Retry) " +
                    "SELECT Id, UserId, VoucherId, EventDate, EventType, Retry FROM Logs",
                "DROP TABLE Logs",
                "ALTER TABLE Logs_New RENAME TO Logs",

                // IMPORTANT
                "UPDATE Parameters SET Value='2' WHERE Key='Migration'"
            );
        }

        public static bool Migration3(DBHelper db)
        {
            return ApplyMigrationQueries(
                db, null,

                "CREATE INDEX Logs_VoucherId_EventType_Id ON Logs(VoucherId, EventType, Id)",

                // IMPORTANT
                "UPDATE Parameters SET Value='3' WHERE Key='Migration'"
           );
        }

        public static bool Migration4(DBHelper db)
        {
            return ApplyMigrationQueries(
                db,
                (db2, trans) =>
                {
                    using (var selectCommand = db2.CreateCommand(
                        "SELECT l.*, v.BatchId FROM Logs l " +
                            "INNER JOIN Voucher v ON l.VoucherId=v.Id",
                        trans))
                    using (var updateVoucherCommand = Utility.CreateVoucherEventCountUpdateCommand(db2, trans))
                    using (var updateBatchCommand = Utility.CreateBatchEventCountUpdateCommand(db2, trans))
                    {
                        using (var reader = selectCommand.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                int printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta;
                                if (Utility.CalculateEventCountDeltas(
                                    (VendorEvent.VendorEventType)int.Parse((string)reader["EventType"]),
                                    (bool)reader["Retry"],
                                    out printCountDelta,
                                    out reprintCountDelta,
                                    out exportCountDelta,
                                    out reExportCountDelta))
                                {
                                    Utility.ExecuteEventCountUpdateQuery(updateVoucherCommand, (int)reader["VoucherId"],
                                        printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta);

                                    Utility.ExecuteEventCountUpdateQuery(updateBatchCommand, (int)reader["BatchId"],
                                        printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta);
                                }
                            }
                        }
                    }
                },

                "ALTER TABLE Logs ADD COLUMN Summarised BOOLEAN DEFAULT 0",
                "CREATE INDEX Logs_Summarised_Id ON Logs(Summarised, Id)",
                "UPDATE Logs SET Summarised=1",

                "ALTER TABLE Batch ADD COLUMN PrintCount INT DEFAULT 0",
                "ALTER TABLE Batch ADD COLUMN ReprintCount INT DEFAULT 0",
                "ALTER TABLE Batch ADD COLUMN ExportCount INT DEFAULT 0",
                "ALTER TABLE Batch ADD COLUMN ReExportCount INT DEFAULT 0",

                "ALTER TABLE Voucher ADD COLUMN PrintCount INT DEFAULT 0",
                "ALTER TABLE Voucher ADD COLUMN ReprintCount INT DEFAULT 0",
                "ALTER TABLE Voucher ADD COLUMN ExportCount INT DEFAULT 0",
                "ALTER TABLE Voucher ADD COLUMN ReExportCount INT DEFAULT 0",

                // IMPORTANT
                "UPDATE Parameters SET Value='4' WHERE Key='Migration'"
           );
        }

        public static bool Migration5(DBHelper db)
        {
            return ApplyMigrationQueries(
                db, null,

                "INSERT INTO Parameters (Key, Value) VALUES ('PrinterInitString', '" + Globals.DefaultPrinterInitString + "')",

                // IMPORTANT
                "UPDATE Parameters SET Value='5' WHERE Key='Migration'"
           );
        }
    }
}
