using Dapper;
using Dapper.Contrib.Extensions;
using MySqlConnector;
using Newtonsoft.Json;
using Quizzer.Extensions;
using Quizzer.Models;
using Quizzer.OpenIDConnect;
using System.Diagnostics;

namespace Quizzer.Persistens
{
    public class DbOps
    {
        #region General

        private Boolean isDebug { get; set; }

        public DbOps()
        {
            isDebug = MyConfiguration.Get()["DEBUG"] == "Y";
        }

        private string GetConnectionString()
        {
            string myConnectionString = MyConfiguration.Get()["QUIZZER_DBCONNECTION"];
            if (myConnectionString == null)
            {
                if (isDebug) Console.WriteLine("Connectionstring: Default");
                return ("server=l3.ucnit.eu;user id=quizzer;password=12tf56so;persistsecurityinfo=True;database=Q");
            }
            else
            {
                if (isDebug) Console.WriteLine("Connectionstring: " + myConnectionString);
                return myConnectionString;
            }
        }

        private void CreateIfNotExist(MySqlConnection myDbConnection)
        {
            // Create tables if not there
            myDbConnection.Open();
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Cats` (   `CId` varchar(50) NOT NULL DEFAULT 'NA',   `UID` varchar(256) NOT NULL DEFAULT '/',   `CParentId` varchar(50) NOT NULL DEFAULT '/',   `Txt` varchar(50) NOT NULL DEFAULT 'NA',   `Descr` varchar(1024) NOT NULL DEFAULT 'NA',   PRIMARY KEY (`CId`),   KEY `TId` (`CId`) ) ENGINE=InnoDB COLLATE='utf8mb4_general_ci'");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Journals` (   `PID` varchar(256) NOT NULL,   `QuizzID` varchar(50) NOT NULL,   `Category` varchar(50) NOT NULL,   `IsOpen` int(11) NOT NULL DEFAULT 1,   `Score` int(11) NOT NULL,   `ScoreAcc` int(11) NOT NULL,   `JournalJson` varchar(2048) NOT NULL,   PRIMARY KEY (`PID`,`QuizzID`) USING BTREE ) ENGINE=InnoDB COLLATE='utf8mb4_general_ci'");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Questions` (   `QuestionID` varchar(50) NOT NULL DEFAULT 'ERROR',   `QuestionType` varchar(50) NOT NULL DEFAULT '',   `Category` varchar(50) NOT NULL DEFAULT '/',   `QuestionText` varchar(512) NOT NULL DEFAULT '0',   `ContentJson` varchar(1024) NOT NULL DEFAULT '0',   PRIMARY KEY (`QuestionID`) ) ENGINE=InnoDB COLLATE='utf8mb4_general_ci'");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Quizzs` (   `QuizzId` varchar(50) NOT NULL DEFAULT 'na',   `Owner` varchar(256) NOT NULL DEFAULT 'na',   `Class` varchar(50) NOT NULL DEFAULT 'na',   `Category` varchar(50) DEFAULT NULL,   `Descr` varchar(1024) DEFAULT NULL,   `Anonymous` int(11) DEFAULT 0,   `QSize` int(10) unsigned NOT NULL DEFAULT 5,   PRIMARY KEY (`QuizzId`) USING BTREE ) ENGINE=InnoDB COLLATE='utf8mb4_general_ci'");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Statistics` (   `QuizzID` varchar(50) DEFAULT 'na',   `Version` int(11) DEFAULT 0,   `StatsJson` varchar(8192) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL ) ENGINE=InnoDB COLLATE='utf8mb4_general_ci'");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `TheClasss` (   `Name` varchar(50) DEFAULT NULL ) ENGINE=InnoDB COLLATE='utf8mb4_general_ci'");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Coverage` ( `Email` VARCHAR(50) NOT NULL DEFAULT 'ERR' COLLATE 'utf8mb4_general_ci', `Name` VARCHAR(50) NOT NULL DEFAULT 'ERR' COLLATE 'utf8mb4_general_ci', `Date` VARCHAR(50) NOT NULL DEFAULT 'ERR' COLLATE 'utf8mb4_general_ci', `TheClass` VARCHAR(50) NOT NULL DEFAULT 'ERR' COLLATE 'utf8mb4_general_ci', PRIMARY KEY (`Email`, `Name`, `Date`) USING BTREE ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Images` ( `MD5` VARCHAR(36) NOT NULL DEFAULT 'ERR', `UID` VARCHAR(50) NOT NULL DEFAULT 'ERR', `Encoding` VARCHAR(16) NULL DEFAULT NULL, `Txt` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`MD5`, `UID`) ) COLLATE='utf8mb4_general_ci'");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Journal2s` (`PID` VARCHAR(256) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`QuizzID` VARCHAR(50) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`Category` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci',`IsOpen` INT(11) NULL DEFAULT NULL,`Anonymous` INT(11) NULL DEFAULT NULL,`Score` INT(11) NULL DEFAULT NULL,`ScoreAcc` INT(11) NULL DEFAULT NULL,PRIMARY KEY (`PID`, `QuizzID`) USING BTREE) COLLATE='utf8mb4_general_ci'");
            myDbConnection.Execute("CREATE TABLE IF NOT EXISTS `Journal2Items` (`PID` VARCHAR(256) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`QuizzID` VARCHAR(50) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`QuestionID` VARCHAR(50) NOT NULL DEFAULT 'ERROR' COLLATE 'utf8mb4_general_ci',`IsOpen` INT(11) NULL DEFAULT NULL,`Score` INT(11) NULL DEFAULT NULL,`MaxScore` INT(11) NULL DEFAULT NULL,`ReplyMCJson` VARCHAR(512) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci',PRIMARY KEY (`PID`, `QuizzID`, `QuestionID`) USING BTREE ) COLLATE='utf8mb4_general_ci'");
        }

        private string GetGuid()
        {
            return System.Guid.NewGuid().ToString();
        }

        #endregion General


        #region Question

        public List<Question> GetQuestions()
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                return myDbConnection.GetAll<Question>().AsList();
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        public Question GetQuestion(string questionID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                return myDbConnection.QueryFirstOrDefault<Question>("SELECT * FROM Questions WHERE QuestionID = @questionID", new
                {
                    questionID
                });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        public List<Question> GetQuestions(string category)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                return myDbConnection.Query<Question>("SELECT * FROM Questions WHERE Category = @theCat ORDER BY QuestionText ASC", new
                {
                    theCat = category
                }).AsList();
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        List<Question> GetQuestionList()
        {
            List<Question> myList = new List<Question>();
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myList = myDbConnection.Query<Question>("SELECT * FROM Questions", new
                {
                }).AsList<Question>();
                return myList;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        /// <summary>
        /// Exports questions in a category in Eiken format.
        /// Takes GUID as parameter
        /// </summary>
        /// <param name="Cat"></param>
        /// <returns></returns>
        public string ExportQuestionsEiken(string Cat)
        {
            string result = "";
            List<MCQuestion> myList = new List<MCQuestion>();
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myList = myDbConnection.Query<MCQuestion>("SELECT * FROM Questions WHERE Category = @category", new
                {
                    category = Cat,
                }).AsList<MCQuestion>();
                foreach (MCQuestion question in myList)
                {
                    question.AList = JsonConvert.DeserializeObject<List<Alternative>>(question.ContentJson);
                    char count = 'A';
                    char answer = 'A';
                    result += question.QuestionText + "\n";
                    foreach (Alternative theA in question.AList)
                    {
                        result += count + ". " + theA.Atxt + "\n";
                        if (theA.Aok) answer = count;
                        count++;
                    }
                    result += "ANSWER: " + answer + "\n\n";
                }
                return result;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        public void DeleteQuestion(string questionID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Execute("DELETE FROM Questions WHERE QuestionID = @questionID", new
                {
                    questionID = questionID
                });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return;
        }

        #endregion Question


        #region MCQuestion

        public Boolean InsertMCQuestion(MCQuestion newQuestion)
        {
            newQuestion.QuestionID = GetGuid();
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                Question dbQuestion = new Question();
                dbQuestion.QuestionID = newQuestion.QuestionID;
                dbQuestion.Category = newQuestion.Category;
                dbQuestion.QuestionText = newQuestion.QuestionText;
                dbQuestion.QuestionType = "MC";
                for (int i = newQuestion.AList.Count - 1; i >= 0; i--)
                {
                    if (newQuestion.AList[i].Atxt == null)
                    {
                        newQuestion.AList.RemoveAt(i);
                        continue;
                    }
                    if (newQuestion.AList[i].Atxt == "")
                    {
                        newQuestion.AList.RemoveAt(i);
                        continue;
                    }
                }
                dbQuestion.ContentJson = JsonConvert.SerializeObject(newQuestion.AList, Formatting.Indented);
                // Is this a copy ??? ---
                Question duplicate = myDbConnection.QueryFirstOrDefault<Question>("SELECT * FROM Questions WHERE Category=@category AND QuestionText=@questionText AND ContentJson=@contentJson", new
                {
                    category = dbQuestion.Category,
                    questionText = dbQuestion.QuestionText,
                    contentJson = dbQuestion.ContentJson,
                });
                if (duplicate != null) return true; // This is a duplicate, so ignore it
                // Is this a copy ??? ===
                //myDbConnection.Insert(dbQuestion);
                int rowsAffected = myDbConnection.Execute("INSERT INTO Questions (QuestionID, QuestionType, Category, QuestionText, ContentJson) VALUES(@QuestionID, @QuestionType, @Category, @QuestionText, @ContentJson)", dbQuestion);
                return true;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return false;
        }

        public MCQuestion GetMCQuestion(string theQId)
        {
            MCQuestion result = null;
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                result = myDbConnection.QueryFirstOrDefault<MCQuestion>("SELECT * FROM Questions WHERE QuestionID=@myQId", new
                {
                    myQId = theQId
                });
                if (result != null)
                {
                    result.AList = JsonConvert.DeserializeObject<List<Alternative>>(result.ContentJson);
                    return result;
                }
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        #endregion MCQuestion


        //public Boolean InsertQuestion(Question newQuestion)
        //{
        //    newQuestion.QuestionID = GetGuid();
        //    try
        //    {
        //        myDbConnection.Execute("INSERT INTO Questions (QId,Txt) VALUES (@myQId, @myTxt);", new
        //        {
        //            myQId = newQuestion.QuestionID,
        //            myTxt = newQuestion.QuestionText
        //        });
        //        for (int i = newQuestion.AList.Count - 1; i >= 0; i--)
        //        {
        //            if (newQuestion.AList[i].Alternative == null)
        //            {
        //                newQuestion.AList.RemoveAt(i);
        //                continue;
        //            }
        //            newQuestion.AList[i].AId = GetGuid();
        //            newQuestion.AList[i].QRef = newQuestion.QuestionID;
        //        }
        //        myDbConnection.Insert(newQuestion.AList);
        //        return true;
        //    }
        //    catch (SqlException ex)
        //    {
        //        Console.WriteLine(ex.Message);
        //    }
        //    return false;
        //}


        #region Category

        private string indentString = "&nbsp;&nbsp;&nbsp;";

        public string MakeCSubList(string asForm, string indent, string curSelect, ref List<Cat> cats, string theParent)
        {
            string myList = string.Empty;
            string selTxt;
            foreach (var item in cats)
            {
                if (item.CParentId == theParent)
                {
                    string subList = MakeCSubList(asForm, indent + indentString, curSelect, ref cats, item.CId);
                    switch (asForm)
                    {
                        case "radioBtn":
                            if (item.CId == curSelect)
                                selTxt = " checked";
                            else
                                selTxt = "";
                            myList += "<div class=\"form - check\">\n" +
                                      "  <input class=\"form - check - input\" type =\"radio\" name =\"categoryParent\" id =\"" +
                                      item.CId +
                                      "\" value =\"" +
                                      item.CId +
                                      "\" onclick=\"javascript: this.form.submit()\"" + selTxt + " > \n" +
                                      "  <label class=\"form - check - label\" for=\"" + item.CId + "\" > \n" + indent + item.Txt + "  </label>\n" +
                                      "</div>\n";
                            if (subList != string.Empty)
                                myList += subList;
                            break;
                        case "DropDown":
                        case "DropDownIndented":
                            if (item.CId == curSelect)
                                selTxt = " selected";
                            else
                                selTxt = "";
                            myList = myList + "<option value=\"" + item.CId + "\"" + selTxt + " >" + indent + item.Txt + " </option >";
                            if (subList != string.Empty)
                                myList += subList;
                            break;
                        default:
                            break;
                    }

                }
            }
            return myList;
        }
        public string MakeCList(string uid, string asForm, string curSelect)
        {
            DbOps qManager = new();
            List<Cat> cats = qManager.GetCats(uid);
            switch (asForm)
            {
                case "radioBtn":
                    //return "<ul>" + MakeCSubList(asForm, "&nbsp;&nbsp", curSelect, ref cats, "/") + "</ul>";
                    return "<ul>" + MakeCSubList(asForm, "", curSelect, ref cats, "/") + "</ul>";
                case "DropDown":
                    return "<select name=\"newCategory\" onchange=\"javascript: this.form.submit()\">" +
                        "<option value=\"\">Please select</option>" +
                        MakeCSubList(asForm, "&nbsp;&nbsp", curSelect, ref cats, "/") +
                        "</select>";
                case "DropDownIndented":
                    return "<select name=\"newCategory\" onchange=\"javascript: this.form.submit()\">" +
                        "<option value=\"\">Please select</option>" +
                        MakeCSubList(asForm, "&nbsp;&nbsp;", curSelect, ref cats, "/") +
                        "</select>";
                default:
                    return "<p>MakeCList form error: " + asForm + "</p>";
            }
        }

        public Cat SetCat(string uid, string parentTagID, string newTagName)
        {
            Cat myCat = new();
            myCat.CParentId = parentTagID;
            myCat.UID = uid;
            myCat.Txt = newTagName;
            myCat.CId = GetGuid();
            // doesn't map TId fild    // myDbConnection.Insert<Tag>(myTag);
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                int rowsAffected = myDbConnection.Execute("INSERT INTO Cats (CId, UID, CParentID, Txt) VALUES(@CId, @UID, @CParentID, @Txt)", myCat);
                return myCat;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        public List<Cat> GetCats(string uid)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                return myDbConnection.Query<Cat>("SELECT * FROM Cats WHERE UID = @uid ORDER by Txt ASC", new
                {
                    uid = uid,
                }).AsList();
                //return myDbConnection.GetAll<Cat>().AsList();
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        /// <summary>
        /// Retrieves category record based on GUID
        /// </summary>
        /// <param name="myCId">GUID</param>
        /// <returns></returns>
        public Cat GetCat(string myCId)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                return (Cat)myDbConnection.QueryFirstOrDefault<Cat>("SELECT * FROM Cats WHERE CId=@myCId", new
                {
                    myCId,
                });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        public List<Cat> GetChildCategories(string parentID)
        {
            List<Cat> myCList = new();
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myCList = myDbConnection.Query<Cat>("SELECT * FROM Cats WHERE CParentId=@myPId", new
                {
                    myPId = parentID
                }).AsList<Cat>();
                return myCList;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        public void UpdateCatdescr(string theId, string newDescr)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Execute("UPDATE Cats SET Descr = @descr WHERE CId = @id", new
                {
                    id = theId,
                    descr = newDescr
                });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public void DeleteCategory(string theId)
        {
            List<Cat> cats = null;
            // WARNING: Recursivly delete child categories
            // Isolate this SQL query as it goes recursive
            using (var myDbConnection = new MySqlConnection(GetConnectionString()))
            {
                try
                {
                    myDbConnection.Open();
                    cats = myDbConnection.Query<Cat>("SELECT * from Cats WHERE CParentID=@CParentID", new
                    {
                        CParentID = theId,
                    }).AsList<Cat>();
                    myDbConnection.Close();
                }
                catch (MySqlException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            if (cats.Count != 0)
            {
                foreach (var item in cats)
                {
                    DeleteCategory(item.CId);
                }
            }
            // Now delete all questions in that category
            List<Question> qs = GetQuestions(theId);
            foreach (var item in qs)
            {
                DeleteQuestion(item.QuestionID);
            }
            // Finally delete the category itself

            using (var myDbConnection = new MySqlConnection(GetConnectionString()))
            {
                try
                {
                    myDbConnection.Open();
                    myDbConnection.Execute("DELETE FROM Cats WHERE CId=@cid", new
                    {
                        cid = theId,
                    });
                    myDbConnection.Close();
                }
                catch (MySqlException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        #endregion Category


        #region Quizz

        public bool InsertQuizz(Quizz newQuizz)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                Quizz myQuizz = JsonConvert.DeserializeObject<Quizz>(JsonConvert.SerializeObject(newQuizz));
                myQuizz.QuizzId = GetGuid();
                int rowsAffected = myDbConnection.Execute("INSERT INTO Quizzs (QuizzId,Owner,Class,Category,QSize,Anonymous,Descr) VALUES(@QuizzId,@Owner,@Class,@Category,@QSize,@Anonymous,@Descr)", myQuizz);
                return true;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }

        public Quizz GetQuizz(string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                List<Quizz> dbResult = myDbConnection.Query<Quizz>("SELECT * FROM Quizzs WHERE QuizzId = @QuizzId", new { QuizzId = quizzID }).AsList();
                if (dbResult.Count == 1) return dbResult[0];
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        /// <summary>
        /// Get all quizzes assigned to a class
        /// </summary>
        /// <param name="theClass"></param>
        /// <returns></returns>
        public List<Quizz> GetQuizzesClass(string theClass)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                return myDbConnection.Query<Quizz>("SELECT * FROM Quizzs WHERE Class = @theClass", new { theClass = theClass }).AsList();
            }
            catch (Exception)
            {

                CreateIfNotExist(myDbConnection);
                return myDbConnection.Query<Quizz>("SELECT * FROM Quizzs WHERE Class = @theClass", new { theClass = theClass }).AsList();
            }
        }

        /// <summary>
        /// Get all quizzes owned by the teacher
        /// </summary>
        /// <param name="theTeacher"></param>
        /// <returns></returns>
        public List<QuizzDN> GetQuizzesTeacher(string theTeacher)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                List<QuizzDN> theQuizzes = myDbConnection.Query<QuizzDN>("SELECT * FROM Quizzs WHERE Owner = @theOwner", new { theOwner = theTeacher }).AsList();
                foreach (var item in theQuizzes)
                {
                    Cat theCat = GetCat(item.Category);
                    item.CategoryText = theCat.Txt;
                }
                return theQuizzes;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        /// <summary>
        /// Get all quizzes to present to teacher
        /// </summary>
        /// <param name="doJoin">
        /// Boolean, if true join category textfield
        /// </param>
        /// <returns></returns>
        public List<QuizzDN> GetQuizzes()
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                List<QuizzDN> theQuizzes = myDbConnection.GetAll<QuizzDN>().AsList();
                foreach (var item in theQuizzes)
                {
                    Cat theCat = GetCat(item.Category);
                    item.CategoryText = theCat.Txt;
                }
                return theQuizzes;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        public void DeleteQuizz(string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Execute("DELETE FROM Quizzs WHERE QuizzID = @QuizzID", new { QuizzID = quizzID });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
        }

        #endregion Quizz


        #region Journal

        /// <summary>
        /// Get the journal for a specific quiz and a specific user. Returns null if no journal exists for that quiz and user.
        /// </summary>
        /// <param name="thePID"></param>
        /// <param name="theQuizzID"></param>
        /// <returns></returns>
        public Journal GetJournal(string thePID, string theQuizzID)
        {
            string sql = "SELECT * FROM Journals WHERE PID = '" + thePID + "' AND QuizzID = '" + theQuizzID + "'";
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                var myResult = myDbConnection.QueryFirstOrDefault<Journal>(sql);
                if (myResult != null)
                {
                    string tmp = myResult.JournalJson.Decompress();
                    myResult.JournalJson = tmp;
                }
                return myResult;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Get all journals for a specific user. Returns null if no journal exists for that user.
        /// </summary>
        /// <param name="thePID"></param>
        /// <returns></returns>
        public List<Journal> GetJournal(string thePID)
        {
            string sql = "SELECT * FROM Journals WHERE PID = '" + thePID + "'";
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                List<Journal> result = myDbConnection.Query<Journal>(sql).AsList();
                return result;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }

        /// <summary>
        /// Update journal. Journal is identified by PID and QuizzID. If no journal exists for that PID and QuizzID, nothing happens.
        /// </summary>
        /// <param name="theJournal"></param>
        public void UpdateJournal(Journal theJournal)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                int count = myDbConnection.Execute("UPDATE Journals SET JournalJson = @JournalJson, IsOpen = @IsOpen, Score = @Score, Anonymous = @Anonymous WHERE PID=@PID AND QuizzID = @QuizzID", new
                {
                    JournalJson = theJournal.JournalJson.Compress(),
                    PID = theJournal.PID,
                    QuizzID = theJournal.QuizzID,
                    Anonymous = theJournal.Anonymous,
                    IsOpen = theJournal.IsOpen,
                    Score = theJournal.Score,
                });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
        }

        /// <summary>
        /// Insert journal. If a journal already exists for that PID and QuizzID, nothing happens.
        /// </summary>
        /// <param name="theJournal"></param>
        public void PostJournal(Journal theJournal)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Execute("INSERT INTO Journals (PID, QuizzID, Category, Anonymous, IsOpen, Score, ScoreAcc, JournalJson) VALUES(@PID,@QuizzID,@Category,@Anonymous,@IsOpen,@Score,@ScoreAcc,@JournalJson)", new
                {
                    PID = theJournal.PID,
                    QuizzID = theJournal.QuizzID,
                    Category = theJournal.Category,
                    Anonymous = theJournal.Anonymous,
                    IsOpen = theJournal.IsOpen,
                    Score = theJournal.Score,
                    ScoreAcc = theJournal.ScoreAcc,
                    JournalJson = theJournal.JournalJson.Compress(),
                });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
        }

        /// <summary>
        /// Get all journals for a specific quiz. Returns null if no journal exists for that quiz.
        /// </summary>
        /// <param name="quizzID"></param>
        /// <returns></returns>
        public List<Journal> GetJournals(string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                return myDbConnection.Query<Journal>("SELECT * FROM Journals WHERE QuizzID = @quizzID", new { quizzID }).AsList();
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }

        /// <summary>
        /// Deletes the journal entry associated with the specified quiz identifier.
        /// </summary>
        /// <param name="quizzID">The unique identifier of the quiz whose journal entry is to be deleted.</param>
        public void DeleteJournalByQuizz(string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Execute("DELETE FROM Journals WHERE QuizzID = @QuizzID", new { QuizzID = quizzID });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Deletes the journal entry associated with the specified participant identifier and quiz identifier.
        /// </summary>
        /// <param name="pID"></param>
        /// <param name="quizzID"></param>
        public void DeleteJournal(string pID, string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try { myDbConnection.Open(); }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            myDbConnection.Execute("DELETE FROM Journals WHERE PID = @PID AND QuizzID = @QuizzID", new { PID = pID, QuizzID = quizzID });
        }

        #endregion Journal

        #region Journal2

        // JOURNAL2 is an alternative journal table with same structure as Journal, but with different name. This is used for testing purposes, to avoid conflicts with the original Journal table. It is not used in production.

        /// <summary>
        /// Get the journal for a specific quiz and a specific user. Returns null if no journal exists for that quiz and user.
        /// </summary>
        /// <param name="thePID"></param>
        /// <param name="theQuizzID"></param>
        /// <returns></returns>
        public Journal2 GetJournal2(string thePID, string theQuizzID)
        {
            string sqlJ = $"SELECT * FROM Journal2s WHERE PID = '{thePID}' AND QuizzID = '{theQuizzID}'";
            string sqlI = $"SELECT * FROM Journal2Items WHERE PID = '{thePID}' AND QuizzID = '{theQuizzID}'";
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                // Get the journal entry, general part.
                var theJournal = myDbConnection.QueryFirstOrDefault<Journal2>(sqlJ);
                if (theJournal == null) return null;
                // Get the journal items.
                theJournal.Items = myDbConnection.Query<Journal2Item>(sqlI).AsList();
                //theJournal.Items.Sort((x, y) => x.QuestionID.CompareTo(y.QuestionID));
                if (theJournal.Items != null)
                {
                    foreach (var item in theJournal.Items)
                    {
                        item.ReplyMC = JsonConvert.DeserializeObject<List<bool>>(item.ReplyMCJson);
                    }
                }
                return theJournal;
            }
            catch
            {
                myDbConnection.Close();
                CreateIfNotExist(myDbConnection);   // must be in cosed state
                return null;
            }
        }

        /// <summary>
        /// Get all journals for a specific user. Returns null if no journal exists for that user.
        /// </summary>
        /// <param name="thePID"></param>
        /// <returns></returns>
        public List<Journal2> GetJournal2(string thePID)
        {
            string sqlJ = $"SELECT * FROM Journal2s WHERE PID = '{thePID}'";
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                List<Journal2> result = myDbConnection.Query<Journal2>(sqlJ).AsList();
                return result;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }

        /// <summary>
        /// Update journal. Journal is identified by PID and QuizzID. If no journal exists for that PID and QuizzID, nothing happens.
        /// </summary>
        /// <param name="theJournal"></param>
        public void UpdateJournal2(Journal2 theJournal)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Execute("UPDATE Journal2s SET IsOpen = @IsOpen, Score = @Score WHERE PID=@PID AND QuizzID = @QuizzID", theJournal);
                foreach (var item in theJournal.Items)
                {
                    item.ReplyMCJson = JsonConvert.SerializeObject(item.ReplyMC);
                    myDbConnection.Execute("UPDATE Journal2Items SET IsOpen = @IsOpen, Score = @Score, MaxScore = @MaxScore, ReplyMCJson = @ReplyMCJson WHERE PID = @PID AND QuizzID = @QuizzID AND QuestionID = @QuestionID", item);
                }
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
        }

        /// <summary>
        /// Insert journal. If a journal already exists for that PID and QuizzID, nothing happens.
        /// </summary>
        /// <param name="theJournal"></param>
        public void PostJournal2(Journal2 theJournal)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Execute("INSERT INTO Journal2s (PID, QuizzID, Category, Anonymous, IsOpen, Score, ScoreAcc) VALUES(@PID,@QuizzID,@Category,@Anonymous,@IsOpen,@Score,@ScoreAcc)", new
                {
                    PID = theJournal.PID,
                    QuizzID = theJournal.QuizzID,
                    Category = theJournal.Category,
                    Anonymous = theJournal.Anonymous,
                    IsOpen = theJournal.IsOpen,
                    Score = theJournal.Score,
                    ScoreAcc = theJournal.ScoreAcc,
                });

                // Must populate the Journal2Items table with the items of the journal
                if (theJournal.Items != null)
                {
                    foreach (var item in theJournal.Items)
                    {
                        item.PID = theJournal.PID;
                        item.QuizzID = theJournal.QuizzID;
                        item.ReplyMCJson = JsonConvert.SerializeObject(item.ReplyMC);
                        myDbConnection.Execute("INSERT INTO Journal2Items (PID, QuizzID, QuestionID, IsOpen, Score, MaxScore, ReplyMCJson) VALUES(@PID, @QuizzID, @QuestionID, @IsOpen, @Score, @MaxScore, @ReplyMCJson)", item);
                    }
                }
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
        }

        /// <summary>
        /// Deletes the journal entry associated with the specified quiz identifier.
        /// </summary>
        /// <param name="quizzID">The unique identifier of the quiz whose journal entry is to be deleted.</param>
        public void DeleteJournalByQuizz2(string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Delete(quizzID);
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Deletes the journal entry associated with the specified participant identifier and quiz identifier.
        /// </summary>
        /// <param name="pID"></param>
        /// <param name="quizzID"></param>
        public void DeleteJournal2(string pID, string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try { myDbConnection.Open(); }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            myDbConnection.Execute("DELETE FROM Journal2s WHERE PID = @PID AND QuizzID = @QuizzID", new { PID = pID, QuizzID = quizzID });
            myDbConnection.Execute("DELETE FROM Journal2Items WHERE PID = @PID AND QuizzID = @QuizzID", new { PID = pID, QuizzID = quizzID });
        }

        #endregion Journal2


        #region Statistic

        public void UpdateStats(string QuizzID, string QuestionID, List<int> counter)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try { myDbConnection.Open(); }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            for (; ; )
            {
                Statistic theStats = myDbConnection.QueryFirstOrDefault<Statistic>("SELECT * FROM Statistics WHERE QuizzID = @quizzID", new { QuizzID });
                if (theStats != null)
                { // exists - so we must update
                    try
                    {
                        List<StatisticsMCItem> theQStats = JsonConvert.DeserializeObject<List<StatisticsMCItem>>(theStats.StatsJson);
                        int QIndx = theQStats.FindIndex(a => a.QuestionID == QuestionID);
                        if (QIndx != -1)
                        { // Update
                            for (int indx = 0; indx < theQStats[QIndx].Counter.Count; indx++)
                            {
                                theQStats[QIndx].Counter[indx] += counter[indx];
                            }
                        }
                        else
                        { // Create
                            StatisticsMCItem newEntry = new();
                            newEntry.QuestionID = QuestionID;
                            newEntry.Counter = counter;
                            theQStats.Add(newEntry);
                        }
                        int curVersion = theStats.Version++;
                        theStats.StatsJson = JsonConvert.SerializeObject(theQStats);
                        int rowsAffected = myDbConnection.Execute("UPDATE Statistics SET Version=@newVersion,StatsJson=@StatsJson WHERE QuizzID=@QuizzID AND Version=@oldVersion", new { QuizzID = QuizzID, oldVersion = curVersion, StatsJson = theStats.StatsJson, newVersion = theStats.Version });
                        if (rowsAffected > 0) return;
                        Console.WriteLine("Database update conflict");
                    }
                    catch (MySqlException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
                else
                { // new - so we create
                    try
                    {
                        int Version = 1;
                        // Create
                        StatisticsMCItem newEntry = new();
                        newEntry.QuestionID = QuestionID;
                        newEntry.Counter = counter;
                        List<StatisticsMCItem> theQStats = new();
                        theQStats.Add(newEntry);
                        string StatsJson = JsonConvert.SerializeObject(theQStats);
                        int rowsAffected = myDbConnection.Execute("INSERT INTO Statistics (QuizzID,Version,StatsJson) VALUES(@QuizzID,@Version,@StatsJson)", new { QuizzID = QuizzID, Version, StatsJson });
                        if (rowsAffected > 0) return;
                    }
                    catch (MySqlException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }

                }
            }
        }

        public StatisticsMCDN GetStatisticsMCDN(string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try { myDbConnection.Open(); }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
            StatisticsMCDN theQuizzStat = myDbConnection.QueryFirstOrDefault<StatisticsMCDN>("SELECT * FROM Statistics WHERE QuizzID=@QuizzID", new
            {
                QuizzID = quizzID
            });
            // if no statistics - tough luck
            if (theQuizzStat == null) return null;
            StatisticsMCDN theQStats = new();
            theQStats.MCList = JsonConvert.DeserializeObject<List<StatisticsMCItem>>(theQuizzStat.StatsJson);
            foreach (StatisticsMCItem myQ in theQStats.MCList)
            {
                theQStats.Questions.Add(GetMCQuestion(myQ.QuestionID));
            }
            return theQStats;
        }

        public void DeleteStatistics(string quizzID)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try { myDbConnection.Open(); }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            myDbConnection.Execute("DELETE FROM Statistics WHERE QuizzID = @QuizzID", new { QuizzID = quizzID });
        }

        #endregion Statistic


        #region TheClass

        public List<TheClass> GetTheClasses()
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try { myDbConnection.Open(); }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
            return myDbConnection.GetAll<TheClass>().AsList();
        }

        public bool CreateTheClass(TheClass newClass)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myDbConnection.Execute("INSERT INTO TheClasss (Name) VALUES(@Name)", newClass);
                return true;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }

        #endregion TheClass


        #region Coverage

        public void SetCoverage(string Email, string Name, string TheClass)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                int rows = myDbConnection.Execute("INSERT INTO Coverage (Email, Name, Date, TheClass) VALUES(@Email, @Name, @Date, @TheClass)", new
                {
                    Email,
                    Name,
                    TheClass,
                    Date = DateTime.Now.ToString("yyyy.MM.dd"),

                });
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public List<Coverage> GetCoverage(string TheClass)
        {
            List<Coverage> myList = new List<Coverage>();
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try
            {
                myDbConnection.Open();
                myList = myDbConnection.Query<Coverage>("SELECT * FROM Coverage WHERE TheClass=@TheClass ORDER BY TheClass ASC, Email ASC, Date ASC", new
                {
                    TheClass,
                }).AsList<Coverage>();
                return myList;
            }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

        public List<string> GetCoverageDates(string TheClass)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try { myDbConnection.Open(); }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
            return myDbConnection.Query<string>("SELECT DISTINCT Date FROM Coverage WHERE TheClass = @TheClass ORDER BY Date ASC", new
            {
                TheClass,
            }).AsList();
        }

        public List<Coverage> GetCoveragePersons(string TheClass)
        {
            using var myDbConnection = new MySqlConnection(GetConnectionString());
            try { myDbConnection.Open(); }
            catch (MySqlException ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
            return myDbConnection.Query<Coverage>("SELECT DISTINCT Email,Name FROM Coverage WHERE TheClass = @TheClass ORDER BY Name ASC", new
            {
                TheClass,
            }).AsList();
        }

        public bool GetCoverageAttendance(Coverage wasPresent)
        {
            return false;
        }

        #endregion Coverage
    }
}
